package com.alibaba.citrus.cr.unified.inventory.facade.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.citrus.cr.common.annotation.FacadeInvoker;
import com.alibaba.citrus.cr.common.constant.SaleOrderConstants;
import com.alibaba.citrus.cr.common.enums.ordercenter.AsyncMessageEventEnum;
import com.alibaba.citrus.cr.common.enums.ordercenter.InnerMessageEventEnum;
import com.alibaba.citrus.cr.common.enums.ordercenter.OrderCategoryEnum;
import com.alibaba.citrus.cr.common.enums.ordercenter.SalesOrderPushStatusEnum;
import com.alibaba.citrus.cr.common.exception.FacadeException;
import com.alibaba.citrus.cr.common.lock.RedisLock;
import com.alibaba.citrus.cr.common.log.ModuleEnum;
import com.alibaba.citrus.cr.common.util.SapAndCrmFieldDealUtils;
import com.alibaba.citrus.cr.unified.inventory.common.SetOrderTag;
import com.alibaba.citrus.cr.unified.inventory.contants.FeaturesKey;
import com.alibaba.citrus.cr.unified.inventory.contants.MessageBusinessCodeEnum;
import com.alibaba.citrus.cr.unified.inventory.contants.MessageEventCodeEnum;
import com.alibaba.citrus.cr.unified.inventory.contants.UnifiedInventoryKey;
import com.alibaba.citrus.cr.unified.inventory.functions.allocateOrder.AllocateOrderEntity;
import com.alibaba.citrus.cr.unified.inventory.functions.allocateOrder.AllocateOrderFactory;
import com.alibaba.citrus.cr.unified.inventory.functions.factory.AllocateOrderEntityFactory;
import com.alibaba.citrus.cr.unified.inventory.functions.service.AllocateOrderCloseService;
import com.alibaba.citrus.cr.unified.inventory.model.*;
import com.alibaba.citrus.cr.unified.inventory.repository.AllocateOrderReadRepository;
import com.alibaba.citrus.cr.unified.inventory.repository.AllocateOrderWriteRepository;
import com.alibaba.citrus.cr.unified.inventory.utils.*;
import com.alibaba.cloud.retail.delivery.api.model.delivery.enums.FulfillmentDirectionTypeEnum;
import com.alibaba.cloud.retail.delivery.api.model.delivery.enums.FulfillmentOrderStatusEnum;
import com.alibaba.cloud.retail.delivery.api.model.delivery.enums.FulfillmentTypeEnum;
import com.alibaba.cloud.retail.delivery.api.model.delivery.enums.OrderTypeEnum;
import com.alibaba.cloud.retail.delivery.api.model.delivery.request.fulfillment.CreateFulfillmentOrderDetailDTO;
import com.alibaba.cloud.retail.delivery.api.model.delivery.request.fulfillment.CreateFulfillmentOrderReq;
import com.alibaba.cloud.retail.delivery.api.model.delivery.request.fulfillment.FinishReq;
import com.alibaba.cloud.retail.delivery.api.model.delivery.response.fulfillment.BatchCloseFulfillmentOrderResponse;
import com.alibaba.cloud.retail.delivery.api.model.delivery.response.fulfillment.FulfillmentGetRes;
import com.alibaba.cloud.retail.delivery.api.model.delivery.response.fulfillment.FulfillmentOrderResponse;
import com.alibaba.cloud.retail.delivery.api.model.fulfillment.request.SearchFulfullmentOrderReq;
import com.alibaba.cloud.retail.delivery.api.service.bcfulfillment.FulfillmentOrderAbilityService;
import com.alibaba.cloud.retail.store.api.service.CargoZoneQueryService;
import com.alibaba.cloud.retail.store.api.service.WarehouseQueryService;
import com.alibaba.cloud.retail.store.request.cargozone.QueryCargoZoneRequest;
import com.alibaba.cloud.retail.store.request.warehouse.WarehouseQueryRequest;
import com.alibaba.cloud.retail.store.response.ResultPager;
import com.alibaba.cloud.retail.wms.api.model.orders.common.WarehousingOrderDTO;
import com.alibaba.cloud.retail.wms.api.model.orders.common.WmsOrderDetailDTO;
import com.alibaba.cloud.retail.wms.api.model.orders.request.WmsOrderQueryRequest;
import com.alibaba.cloud.retail.wms.api.model.orders.response.WarehousingOrdersQueryResponse;
import com.alibaba.cloud.retail.wms.api.service.orders.WarehousingOrderReadService;
import com.alibaba.cz.base.model.store.CargoZoneSDO;
import com.alibaba.cz.base.model.store.WarehouseSDO;
import com.alibaba.cz.base.tool.DateFormatUtil;
import com.alibaba.cz.base.tool.FeatureUtil;
import com.alibaba.cz.base.tool.Nullable;
import com.alibaba.cz.base.tool.StringUtil;
import com.alibaba.cz.base.tool.log.Log;
import com.alibaba.cz.base.tool.log.model.OpLogDateTypeEnum;
import com.alibaba.cz.base.tool.log.model.OpLogModel;
import com.alibaba.cz.base.tool.log.model.OperateType;
import com.alibaba.cz.base.tool.log.model.OperatorType;
import com.alibaba.cz.base.tool.message.ErrorCodeException;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.lattice2.epoch.middleware.cache.runtime.service.EpochCacheService;
import com.alibaba.lattice2.epoch.middleware.mq.runtime.EpochMessageService;
import com.alibaba.lattice2.epoch.sdk.model.Result;
import com.alibaba.lattice2.epoch.user.dto.LoginUser;
import com.alibaba.lattice2.epoch.user.service.EpochLoginUserService;
import com.epoch.app.bcorder.contants.BcOrderConstants;
import com.epoch.app.bcorder.platform_enhance.SaleOrderTag;
import com.epoch.app.crb2btradecenter.api.orderquery.dto.QuerySingleRequest;
import com.epoch.app.crb2btradecenter.api.orderquery.service.OrderQueryService;
import com.epoch.app.crb2btradecenter.api.orderwrite.service.OrderWriteService;
import com.epoch.app.crb2btradecenter.domain.order.dto.QueryByOrderBizIdsRequest;
import com.epoch.app.crb2btradecenter.domain.order.dto.UpdateFeaturesResponse;
import com.epoch.app.crb2btradecenter.domain.order.model.Order;
import com.epoch.app.crb2btradecenter.domain.order.service.OrderService;
import com.epoch.app.crb2btradecenter.dto.request.ModifyOrderRequest;
import com.epoch.app.crb2btradecenter.dto.request.OrderQueryRequest;
import com.epoch.app.crb2btradecenter.dto.response.OrderQueryResponse;
import com.epoch.app.crb2btradecenter.enums.OrderStatus;
import com.epoch.app.crb2btradecenter.model.dto.UpdateFeaturesRequest;
import com.epoch.app.crb2btradecenter.model.enums.OrderOperateType;
import com.epoch.app.crb2btradecenter.sdo.OrderLineSDO;
import com.epoch.app.crb2btradecenter.sdo.OrderSDO;
import com.epoch.app.crunifiedinventory.allocateorderdomain.dto.UpdateStatusRequest;
import com.epoch.app.crunifiedinventory.allocateorderdomain.service.AllocateOrderDomainService;
import com.epoch.app.crunifiedinventory.allocateorderstandardmessage.service.AllocateOrderStandardMessageService;
import com.epoch.app.crunifiedinventory.api.allocateapplyorderread.service.AllocateApplyOrderReadService;
import com.epoch.app.crunifiedinventory.api.allocateorderread.dto.GetBusinessScenarioResponse;
import com.epoch.app.crunifiedinventory.api.allocateorderread.service.AllocateOrderReadService;
import com.epoch.app.crunifiedinventory.api.allocateorderwrite.service.AllocateOrderWriteService;
import com.epoch.app.crunifiedinventory.consignmentorder.model.ConsignmentOrder;
import com.epoch.app.crunifiedinventory.consignmentorder.service.ConsignmentOrderService;
import com.epoch.app.crunifiedinventory.contants.CrUnifiedInventoryConstants;
import com.epoch.app.crunifiedinventory.domain.allocateorder.dto.AllocateOrderLoadListRequest;
import com.epoch.app.crunifiedinventory.domain.allocateorder.dto.*;
import com.epoch.app.crunifiedinventory.domain.allocateorder.model.AllocateOrder;
import com.epoch.app.crunifiedinventory.domain.allocateorder.service.AllocateOrderService;
import com.epoch.app.crunifiedinventory.domain.allocateorderdetail.dto.BatchCloseRequest;
import com.epoch.app.crunifiedinventory.domain.allocateorderdetail.dto.UpdateAllocateOrderDetailResponse;
import com.epoch.app.crunifiedinventory.domain.allocateorderdetail.model.AllocateOrderDetail;
import com.epoch.app.crunifiedinventory.domain.allocateorderdetail.service.AllocateOrderDetailService;
import com.epoch.app.crunifiedinventory.dto.request.AllocateOrderDetailsQueryRequest;
import com.epoch.app.crunifiedinventory.dto.request.AllocateOrderGetMessageRequest;
import com.epoch.app.crunifiedinventory.dto.request.AllocateOrderManualCreateRequest;
import com.epoch.app.crunifiedinventory.dto.request.AllocateOrderNotifyRequest;
import com.epoch.app.crunifiedinventory.dto.request.AllocateOrderReceivedRequest;
import com.epoch.app.crunifiedinventory.dto.request.AllocateOrderRequest;
import com.epoch.app.crunifiedinventory.dto.request.AllocateOrderSalesOrderCreateSAPRequest;
import com.epoch.app.crunifiedinventory.dto.request.AllocateOrderSalesOrderSyncRequest;
import com.epoch.app.crunifiedinventory.dto.request.AllocateOrderSalesOrderUpdateRequest;
import com.epoch.app.crunifiedinventory.dto.request.AllocateOrderSaveRequest;
import com.epoch.app.crunifiedinventory.dto.request.AllocateOrderShippedRequest;
import com.epoch.app.crunifiedinventory.dto.request.AllocateOrderSubmitRequest;
import com.epoch.app.crunifiedinventory.dto.request.AllocateOrderSubmitSAPRequest;
import com.epoch.app.crunifiedinventory.dto.request.AllocateOrderSyncRequest;
import com.epoch.app.crunifiedinventory.dto.request.AllocateOrderUpdateRequest;
import com.epoch.app.crunifiedinventory.dto.request.AllocateOrderWithdrawRequest;
import com.epoch.app.crunifiedinventory.dto.request.QueryAllocateApplyOrderRequest;
import com.epoch.app.crunifiedinventory.dto.response.AllocateApplyOrderDetailDTO;
import com.epoch.app.crunifiedinventory.dto.response.AllocateOrderCancelResponse;
import com.epoch.app.crunifiedinventory.dto.response.AllocateOrderCreateResponse;
import com.epoch.app.crunifiedinventory.dto.response.AllocateOrderDeliveryResponse;
import com.epoch.app.crunifiedinventory.dto.response.AllocateOrderMessageResponse;
import com.epoch.app.crunifiedinventory.dto.response.AllocateOrderNotifyResponse;
import com.epoch.app.crunifiedinventory.dto.response.AllocateOrderReceivedResponse;
import com.epoch.app.crunifiedinventory.dto.response.AllocateOrderResponse;
import com.epoch.app.crunifiedinventory.dto.response.AllocateOrderSalesOrderCreateSAPResponse;
import com.epoch.app.crunifiedinventory.dto.response.AllocateOrderSalesOrderSyncResponse;
import com.epoch.app.crunifiedinventory.dto.response.AllocateOrderSalesOrderUpdateResponse;
import com.epoch.app.crunifiedinventory.dto.response.AllocateOrderSaveResponse;
import com.epoch.app.crunifiedinventory.dto.response.AllocateOrderShippedResponse;
import com.epoch.app.crunifiedinventory.dto.response.AllocateOrderSubmitResponse;
import com.epoch.app.crunifiedinventory.dto.response.AllocateOrderSubmitSAPResponse;
import com.epoch.app.crunifiedinventory.dto.response.AllocateOrderSyncResponse;
import com.epoch.app.crunifiedinventory.dto.response.AllocateOrderUpdateResponse;
import com.epoch.app.crunifiedinventory.dto.response.AllocateOrderWithdrawResponse;
import com.epoch.app.crunifiedinventory.model.dto.AllocateOrderBackwardDTO;
import com.epoch.app.crunifiedinventory.model.dto.AllocateOrderCancelDeliveredRequest;
import com.epoch.app.crunifiedinventory.model.dto.AllocateOrderCancelDeliveredResponse;
import com.epoch.app.crunifiedinventory.model.dto.AllocateOrderCancelReceivedRequest;
import com.epoch.app.crunifiedinventory.model.dto.AllocateOrderCancelReceivedResponse;
import com.epoch.app.crunifiedinventory.model.dto.AllocateOrderCancelRequest;
import com.epoch.app.crunifiedinventory.model.dto.AllocateOrderCancelShippedRequest;
import com.epoch.app.crunifiedinventory.model.dto.AllocateOrderCancelShippedResponse;
import com.epoch.app.crunifiedinventory.model.dto.AllocateOrderCloseDTO;
import com.epoch.app.crunifiedinventory.model.dto.AllocateOrderCloseRequest;
import com.epoch.app.crunifiedinventory.model.dto.AllocateOrderCloseResponse;
import com.epoch.app.crunifiedinventory.model.dto.AllocateOrderCreateRequest;
import com.epoch.app.crunifiedinventory.model.dto.AllocateOrderDeliveryRequest;
import com.epoch.app.crunifiedinventory.model.dto.AllocateOrderFixDeliveryQuantityRequest;
import com.epoch.app.crunifiedinventory.model.dto.AllocateOrderFixDeliveryQuantityResponse;
import com.epoch.app.crunifiedinventory.model.dto.AllocateOrderFixStatusRequest;
import com.epoch.app.crunifiedinventory.model.dto.AllocateOrderFixStatusResponse;
import com.epoch.app.crunifiedinventory.model.dto.AllocateOrderListNotifyRequest;
import com.epoch.app.crunifiedinventory.model.dto.AllocateOrderReturnOrderRequest;
import com.epoch.app.crunifiedinventory.model.dto.AllocateOrderReturnOrderResponse;
import com.epoch.app.crunifiedinventory.model.dto.AllocateOrderScItemDetailDTO;
import com.epoch.app.crunifiedinventory.model.dto.AllocateOrderStandardMessageDTO;
import com.epoch.app.crunifiedinventory.model.dto.BatchRequest;
import com.epoch.app.crunifiedinventory.model.dto.ConsignmentOrderLoadListRequest;
import com.epoch.app.crunifiedinventory.model.dto.NykSaleOrderRise;
import com.epoch.app.crunifiedinventory.model.dto.NykSaleOrderRiseList;
import com.epoch.app.crunifiedinventory.dto.request.*;
import com.epoch.app.crunifiedinventory.dto.response.*;
import com.epoch.app.crunifiedinventory.model.dto.*;
import com.epoch.app.crunifiedinventory.model.enums.AllocateApplyOrderOperationTypeEnum;
import com.epoch.app.crunifiedinventory.model.enums.AllocateOrderStatus;
import com.epoch.app.crunifiedinventory.ouinykfailrecord.dto.BatchCreateRequest;
import com.epoch.app.crunifiedinventory.ouinykfailrecord.service.OuiNykFailRecordService;
import com.epoch.app.crunifiedinventory.sdo.AllocateOrderDetailSDO;
import com.epoch.app.crunifiedinventory.sdo.AllocateOrderSDO;
import com.epoch.app.crunifiedinventory.sdo.AllocateOrderWithOrderDetailsSDO;
import com.epoch.app.crunifiedinventory.sdo.enums.AllocateOrderShipTypeEnums;
import com.epoch.app.crunifiedinventory.sdo.enums.AllocateOrderTypeEnum;
import com.epoch.app.crunifiedinventory.sdo.enums.ReasonForRejectionEnum;
import com.epoch.app.mnordercenter.model.dto.CommonAsyncMessageResponse;
import com.epoch.app.mnordercenter.model.dto.PushOrderInfoToMnSideRequestOrderInfo;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.alibaba.citrus.cr.common.constant.SaleOrderConstants.TAG_ORDER_CENTER;

/**
 * @author zhangguoqing
 * @date 2021/08/25
 */
@Component
@Primary
public class AllocateOrderWriteServiceImpl  implements AllocateOrderWriteService {

    //    private static Log log = LogFactory.getLog(AllocateOrderWriteServiceImpl.class);
    private Log log = Log.getLogger(AllocateOrderWriteServiceImpl.class);
    private static final String CODE = "code";
    private static final String STATUS = "status";
    private static final String TWENTY_STRING = "20";
    private static final String NINETY_STRING = "90";

    @Resource
    AllocateOrderReadRepository allocateOrderReadRepository;
    @Resource
    EpochMessageService epochMessageService;
    @Resource
    @Qualifier("allocateOrderReadServiceImpl")
    AllocateOrderReadService allocateOrderReadService;
    @Resource
    AllocateOrderWriteRepository allocateOrderWriteRepository;
    @Resource
    @Qualifier("allocateApplyOrderReadServiceImpl")
    private AllocateApplyOrderReadService allocateApplyOrderReadService;


    @Resource
    private EpochLoginUserService epochLoginUserService;
    @Autowired
    private OuiNykFailRecordService ouiNykFailRecordService;
    @DubboReference
    private CargoZoneQueryService cargoZoneQueryService;
    @DubboReference
    private WarehouseQueryService warehouseQueryService;
    @DubboReference
    private FulfillmentOrderAbilityService fulfillmentOrderAbilityService;
    @DubboReference
    private WarehousingOrderReadService warehousingOrderReadService;
    @Resource
    private AllocateOrderStandardMessageService allocateOrderStandardMessageService;
    @Resource
    private AllocateOrderCloseService allocateOrderCloseService;
    @Resource
    private AllocateOrderEntityFactory allocateOrderEntityFactory;
    @Resource
    private WarehouseConverter warehouseConverter;
    @Resource
    private AllocateOrderDetailService allocateOrderDetailService;
    @Resource
    private AllocateOrderDomainService allocateOrderDomainService;

    @Resource
    private EpochCacheService epochCacheService;
    @Resource
    ConsignmentOrderService consignmentOrderService;
    @Resource
    AllocateOrderService allocateOrderService;
    @Resource
    private OrderQueryService orderQueryService;
    @Resource
    private OrderWriteService orderWriteService;
    @Resource
    private OrderService orderService;
    @Resource
    private EpochMessageService messageService;
    //调拨单确认数量先后顺序 缓存
    private static final String ALLOCATE_ORDER_CONFIRM_QUANTITY_KEY = "ALLOCATE_ORDER_CONFIRM_QUANTITY_KEY_";
    // 并发锁key前缀
    private static final String ALLOCATE_ORDER_LOCK_SYNC = "allocateOrderLockSync_";
    //
    private static final String TRANSFER_WAREHOUSE_ID = "freightYardCode";

    //设置sap过期时间为
    private static final Long SAP_TIMESTAMP_CACHE_EXPIRE = 60 * 60 * 24 * 100L;

    private static final String CACHE_CODE = "REDIS_CACHE";
    /**
     * 获取锁的超时时间（毫秒）
     */
    private static final long TIMEOUT_MSECS = 2 * 60 * 1000;
    /**
     * 锁过期时间（毫秒）
     */
    private static final long EXPIRE_MSECS = 10 * 60 * 1000;

    /**
     * 循环获取锁的间隔时间（毫秒）
     */
    private static final long INTERNAL = 2000;

    public static final String SAP_SYNC = "sapSync:";

    /**
     * F-B F-b 销售订单更新/ 退单 /创建 013
     * @param request
     * @return
     */
    @Override
    @FacadeInvoker(value = "013同步调拨单", errorCode = "OTS-03-005-00-15-030")
    public Result<AllocateOrderSalesOrderSyncResponse> salesOrderSync(AllocateOrderSalesOrderSyncRequest request) {
        log.info("AllocateOrderWriteServiceImpl.salesOrderSync,data:" + JSON.toJSONString(request));

        String code = null;
        String allocateApplyOrderCode = "";
        Result<AllocateApplyOrderDetailDTO> allocateApplyOrderDetailDTOOld = new Result<>();
        AllocateOrderRequest allocateOrderRequest = new AllocateOrderRequest();
        AllocateOrderWithOrderDetailsSDO sdoOld = new AllocateOrderWithOrderDetailsSDO();
        try(RedisLock lock = new RedisLock(BcOrderConstants.REDIS_CACHE_CODE(),ALLOCATE_ORDER_LOCK_SYNC + request.getOutAllocateOrderId(),5 * 60 * 1000,10 * 60 * 1000)){
            if (!lock.lock()){
                throw new FacadeException("OTS-03-005-00-15-096");
            }

            //if (StringUtils.isNotEmpty(request.getCode())) {
            allocateOrderRequest.setAllocateOrderCode(request.getCode());
            allocateOrderRequest.setOutAllocateOrderId(request.getOutAllocateOrderId());
            log.info("AllocateOrderWriteServiceImpl.salesOrderSync.get,code:{}", request.getCode());
            sdoOld = allocateOrderReadRepository.get(allocateOrderRequest);
            log.info("AllocateOrderWriteServiceImpl.salesOrderSync.get,getResultData:{}", JSON.toJSONString(sdoOld));
            // }
            if (Objects.nonNull(sdoOld)&&Objects.nonNull(sdoOld.getAllocateOrderSDO())){
                allocateApplyOrderCode = sdoOld.getAllocateOrderSDO().getAllocateApplyOrderCode();
            }
            //查询老的调拨预报单
            if (StringUtils.isNotEmpty(allocateApplyOrderCode)) {
                QueryAllocateApplyOrderRequest queryAllocateApplyOrderRequest = new QueryAllocateApplyOrderRequest();
                queryAllocateApplyOrderRequest.setCode(allocateApplyOrderCode);
                allocateApplyOrderDetailDTOOld = allocateApplyOrderReadService.queryAllocateApplyOrderByCode(queryAllocateApplyOrderRequest);
            }
            log.info("AllocateOrderWriteServiceImpl.salesOrderSync.salesOrderSync,code:{},request:{}",request.getCode(),JSON.toJSONString(request));
            //判断时间戳 如果时间
            String modifierTime = epochCacheService.get(BcOrderConstants.REDIS_CACHE_CODE(),
                    ALLOCATE_ORDER_CONFIRM_QUANTITY_KEY + request.getOutAllocateOrderId());
            //下发时间戳
            String ext = request.getExt();
            if (StringUtils.isNotBlank(modifierTime)){
                String[] modifierTimeArray = modifierTime.split("\\.");
                String[] extArray = ext.split("\\.");
                if (Long.parseLong(modifierTimeArray[0]) > Long.parseLong(extArray[0])){
                    AllocateOrderSalesOrderSyncResponse response = new AllocateOrderSalesOrderSyncResponse();
                    response.setCode("当前修改时间早于上一次 不允许更新");
                    return Result.success(response);
                }else if (Long.valueOf(modifierTimeArray[0]).longValue() == Long.valueOf(extArray[0]).longValue()){
                    if (Long.parseLong(modifierTimeArray[1]) >= Integer.valueOf(extArray[1]).longValue()){
                        AllocateOrderSalesOrderSyncResponse response = new AllocateOrderSalesOrderSyncResponse();
                        response.setCode("当前修改时间早于上一次 不允许更新");
                        return Result.success(response);
                    }
                }
            }
            //设置缓存
//            epochCacheService.put(BcOrderConstants.REDIS_CACHE_CODE(),
//                    ALLOCATE_ORDER_CONFIRM_QUANTITY_KEY + request.getOutAllocateOrderId(),
//                    ext, SAP_TIMESTAMP_CACHE_EXPIRE, TimeUnit.SECONDS);
            code = allocateOrderWriteRepository.salesOrderSync(request);
            //013请求成功后放入缓存
            if (StringUtils.isNotBlank(code) && code.startsWith("STO")){
                epochCacheService.put(BcOrderConstants.REDIS_CACHE_CODE(),
                        ALLOCATE_ORDER_CONFIRM_QUANTITY_KEY + request.getOutAllocateOrderId(),
                        ext, SAP_TIMESTAMP_CACHE_EXPIRE, TimeUnit.SECONDS);
            }
            log.info("AllocateOrderWriteServiceImpl.salesOrderSync.salesOrderSync,salesOrderSyncResult:{}",code);
        }catch (Exception e){
            log.error("salesOrderSyncError",e);
            throw new FacadeException("OTS-03-005-00-15-098");
        }

        if (StringUtils.isEmpty(request.getCode())) {
            allocateOrderRequest.setAllocateOrderCode(code);
        }
        AllocateOrderWithOrderDetailsSDO sdoNew = allocateOrderReadRepository.get(allocateOrderRequest);
        if (StringUtils.isNotEmpty(request.getCode())){
            saveDocumentLog(sdoOld,sdoNew, AllocateApplyOrderOperationTypeEnum.CHANGE, OperatorType.INNER_INTERFACE.getOperatorType(),null);
        }else{
            saveDocumentLog(sdoOld,sdoNew, AllocateApplyOrderOperationTypeEnum.CREATE, OperatorType.INNER_INTERFACE.getOperatorType(),null);
        }

        //查询新的调拨预报单
        Result<AllocateApplyOrderDetailDTO> allocateApplyOrderDetailDTONew = new Result<>();
        if (StringUtils.isNotBlank(allocateApplyOrderCode)) {
            QueryAllocateApplyOrderRequest queryAllocateApplyOrderRequest = new QueryAllocateApplyOrderRequest();
            queryAllocateApplyOrderRequest.setCode(allocateApplyOrderCode);
            allocateApplyOrderDetailDTONew = allocateApplyOrderReadService.queryAllocateApplyOrderByCode(queryAllocateApplyOrderRequest);
        }
        //添加调拨预报单单据日志
        if (StringUtils.isNotBlank(allocateApplyOrderCode)) {
            Map<String, String> features = new HashMap<>();
            features.put(CODE, allocateApplyOrderCode);
            if (allocateApplyOrderDetailDTONew.getResult() != null &&
                    allocateApplyOrderDetailDTONew.getResult().getStatus() != null){
                features.put(STATUS, allocateApplyOrderDetailDTONew.getResult().getStatus().toString());
            }
//            features.put(STATUS, allocateApplyOrderDetailDTONew.getResult().getStatus().toString());
            AllocateApplyOrderDetailDTO allocateApplyOrderDetailOld = allocateApplyOrderDetailDTOOld.getResult();
            AllocateApplyOrderDetailDTO allocateApplyOrderDetailNew = allocateApplyOrderDetailDTONew.getResult();
            saveAllocateApplyOrderDocumentLog(allocateApplyOrderDetailOld, allocateApplyOrderDetailNew, Integer.valueOf(AllocateApplyOrderOperationTypeEnum.UPDATE.getValue()), features);
        }

        AllocateOrderSalesOrderSyncResponse response = new AllocateOrderSalesOrderSyncResponse();
        response.setCode(code);
        try{
            if (StringUtils.isNotBlank(code)){
//                AllocateOrderDetailsQueryRequest queryRequest = new AllocateOrderDetailsQueryRequest();
//                queryRequest.setCode(code);
//                Result<AllocateOrderDetailsQueryResponse> query = allocateOrderReadService.query(queryRequest);
                AllocateOrderGetMessageRequest allocateOrderGetMessageRequest = new AllocateOrderGetMessageRequest();
                allocateOrderGetMessageRequest.setCode(code);
                Result<AllocateOrderMessageResponse> allocateOrderMessage = allocateOrderReadService.getAllocateOrderMessage(allocateOrderGetMessageRequest);
                Map<String,String> map = Maps.newHashMap();
                map.put("_CZ_KEY", BcOrderConstants.TENANT_ID());
                epochMessageService.sendMessage("allocate_order_message","CZ_ALLOCATE_V2","AllocateCreate",code, JSON.toJSONString(allocateOrderMessage.getResult()),
                        map);
            }
        }catch(ErrorCodeException e){
            log.error(e,"AllocateOrderWriteServiceImpl.salesOrderSync exception,code:{},msg:{}",e.getErrorCode(),e.getErrorMessage());
            return Result.fail(e.getErrorCode(), e.getErrorMessage());
        }catch (Throwable e){
            log.error("AllocateOrderWriteServiceImpl.salesOrderSync exception:",e);
            // 异常在FacadeInvokeHandler中会处理
            throw e;
        }
        return Result.success(response);
    }

    /**
     * 009 提交预报单到sap
     * @param request
     * @return
     */
    @Override
    @FacadeInvoker(value = "009提交调拨单到SAP消息",errorCode = "OTS-03-005-00-15-041")
    public Result<AllocateOrderSubmitSAPResponse> submitSAP(AllocateOrderSubmitSAPRequest request) {
        log.info("AllocateOrderWriteServiceImpl_submitSAP_009:{}",JSON.toJSONString(request));
        AllocateOrderRequest r = new AllocateOrderRequest();
        r.setAllocateOrderId(request.getId());
        AllocateOrderWithOrderDetailsSDO sdo = allocateOrderReadRepository.get(r);
        String code = "";
        if (sdo != null && sdo.getAllocateOrderSDO() != null){
            code = sdo.getAllocateOrderSDO().getCode();
        }
        Boolean f = epochMessageService.sendMessage("submit_allocate_order_message","CZ_ALLOCATE_V2","submit_sap",
                code,JSON.toJSONString(sdo));
        log.info("AllocateOrderWriteServiceImpl_submitSAP_009_result:{}",f);
        try{
            LoadAllocateOrderRequest loadAllocateOrderRequest = LoadAllocateOrderRequest.builder().id(request.getId()).build();
            AllocateOrder allocateOrder = allocateOrderService.loadAllocateOrder(loadAllocateOrderRequest);
            if (Objects.nonNull(allocateOrder)){
                AllocateOrderStandardMessageDTO messageDTO = new AllocateOrderStandardMessageDTO();
                messageDTO.setKeyword(code);
                messageDTO.setEventCode(MessageEventCodeEnum.SUBMIT.getCode());
                messageDTO.setBusinessCode(MessageBusinessCodeEnum.ALLOCATE_ORDER.getCode());
                messageDTO.setBusinessType(allocateOrder.getBusinessType());
                messageDTO.setFromSys(allocateOrder.getFromSys());
                messageDTO.setEventTime(new Date());
                allocateOrderStandardMessageService.sendAllocateOrderStandardMessage(messageDTO);
            }
        }catch (Throwable e){
            log.error("AllocateOrderStandardMessageService_submit_error",e);
        }
        AllocateOrderSubmitSAPResponse response = new AllocateOrderSubmitSAPResponse();
        return Result.success(response);
    }

    /**
     * 012 F1-B 共享仓到销售单元 货权转移
     * todo 把FacadeInvoker 注解补充完整
     * @param request
     * @return
     */
    @Override
    @FacadeInvoker(value = "012创建总部销售调拨单到SAP消息",errorCode ="OTS-03-005-00-15-040" )
    public Result<AllocateOrderSalesOrderCreateSAPResponse> createSalesOrderSAP(
            AllocateOrderSalesOrderCreateSAPRequest request) {
        log.info("AllocateOrderWriteServiceImpl.createSalesOrderSAP 012提交:" + JSON.toJSONString(request));
        AllocateOrderRequest r = new AllocateOrderRequest();
        r.setAllocateOrderId(request.getId());
        r.setAllocateOrderCode(request.getCode());
        AllocateOrderWithOrderDetailsSDO sdo = allocateOrderReadRepository.get(r);
        String code = "";
        if (sdo != null && sdo.getAllocateOrderSDO() != null){
            code = sdo.getAllocateOrderSDO().getCode();
        }
        Boolean f = epochMessageService.sendMessage("create_sales_order_allocate_order_message","CZ_ALLOCATE_V2","crate_sales_order_sap",
                code,JSON.toJSONString(sdo));
        log.info("AllocateOrderWriteServiceImpl.createSalesOrderSAP.012.result:{}",f);
        AllocateOrderSalesOrderCreateSAPResponse response = new AllocateOrderSalesOrderCreateSAPResponse();
        return Result.success(response);
    }

    /**
     * 总部销售调拨
     * @param request
     * @return
     */
    @Override
    @FacadeInvoker(errorCode = "OTS-03-005-00-15-031",value = "调拨单手动创建")
    public Result<AllocateOrderResponse> manualCreate(AllocateOrderManualCreateRequest request) {
        log.info("AllocateOrderWriteServiceImpl.manualCreate:" + JSON.toJSONString(request));
        //校验渠道编码是不是正确
        if (!(TWENTY_STRING.equals(request.getSapChannel())||NINETY_STRING.equals(request.getSapChannel()))){
            throw new FacadeException("OTS-03-005-00-15-087");
        }
        AllocateOrderResponse response = allocateOrderWriteRepository.manualCreate(request);
        AllocateOrderRequest allocateOrderRequest = new AllocateOrderRequest();
        allocateOrderRequest.setAllocateOrderCode(response.getCode());
        AllocateOrderWithOrderDetailsSDO sdo = allocateOrderReadRepository.get(allocateOrderRequest);
        saveDocumentLogAllocate(new AllocateOrderWithOrderDetailsSDO(),sdo,
                OperateType.CREATE, OperatorType.USER.getOperatorType());
        log.info("AllocateOrderWriteServiceImpl.manualCreate.result:" + JSON.toJSONString(request));
        return Result.success(response);
    }

    /**
     * 调拨单的创建接口. todo 余红伟 资源层加了事务，这里就不要加了
     * @param allocateOrderCreateRequest
     * @return
     */
    @Override
    @FacadeInvoker(errorCode = "OTS-03-005-00-15-032",value = "平台回复创建调拨单")
    public Result<AllocateOrderCreateResponse> create(AllocateOrderCreateRequest allocateOrderCreateRequest) {
        log.info("AllocateOrderWriteServiceImpl.create 平台回复生成调拨单:" + JSON.toJSONString(allocateOrderCreateRequest));

        String id = allocateOrderWriteRepository.create(allocateOrderCreateRequest);
        AllocateOrderRequest allocateOrderRequest = new AllocateOrderRequest();
        allocateOrderRequest.setAllocateOrderId(id);
        AllocateOrderWithOrderDetailsSDO freshSdo = allocateOrderReadRepository.get(allocateOrderRequest);
        saveDocumentLogAllocate(new AllocateOrderWithOrderDetailsSDO(),freshSdo,
                OperateType.CREATE, OperatorType.USER.getOperatorType());

//        CompletableFuture.runAsync(()->{
        try{
            if (StringUtils.isNotBlank(id)){
                AllocateOrderRequest request = new AllocateOrderRequest();
                request.setAllocateOrderId(id);
//                    AllocateOrderWithOrderDetailsSDO sdo = allocateOrderReadRepository.get(request);
//                    saveDocumentLog(new AllocateOrderWithOrderDetailsSDO(),sdo,
//                            OperateType.CREATE.getOperateType(),OperatorType.INNER_INTERFACE.getOperatorType());
                if (Objects.equals(AllocateOrderTypeEnum.HEADQUARTER_SALES.getType(),allocateOrderCreateRequest.getType())){
                    AllocateOrderSalesOrderCreateSAPRequest createSAPRequest = new AllocateOrderSalesOrderCreateSAPRequest();
                    createSAPRequest.setId(id);
                    //发 创建销售订单到sap 012 货权转移
                    createSalesOrderSAP(createSAPRequest);
                }else {
                    AllocateOrderSubmitSAPRequest submitSAPRequest = new AllocateOrderSubmitSAPRequest();
                    submitSAPRequest.setId(id);
                    submitSAP(submitSAPRequest);
                }
//                AllocateOrderDetailsQueryRequest queryRequest = new AllocateOrderDetailsQueryRequest();
//                queryRequest.setAllocateOrderId(id);
//                Result<AllocateOrderDetailsQueryResponse> queryResponseResult = allocateOrderReadService.query(queryRequest);
                AllocateOrderGetMessageRequest allocateOrderGetMessageRequest = new AllocateOrderGetMessageRequest();
                allocateOrderGetMessageRequest.setId(id);
                Result<AllocateOrderMessageResponse> allocateOrderMessage = allocateOrderReadService.getAllocateOrderMessage(allocateOrderGetMessageRequest);
                String code = null;
                if (allocateOrderMessage != null && allocateOrderMessage.getResult() != null){
                    code = allocateOrderMessage.getResult().getCode();
                }
                Map<String,String> map = Maps.newHashMap();
                map.put("_CZ_KEY", BcOrderConstants.TENANT_ID());
                epochMessageService.sendMessage("allocate_order_message","CZ_ALLOCATE_V2","AllocateCreate",
                        code,JSON.toJSONString(allocateOrderMessage.getResult()),
                        map);
            }

        }catch (Throwable e){
            log.error("syncAllocateOrderToSAPError, allocateOrderId:" + id, e);
            throw e;
        }

        AllocateOrderCreateResponse r = new AllocateOrderCreateResponse();
        r.setId(id);
        log.info("AllocateOrderWriteServiceImpl.create 平台回复生成调拨单 restult:" + JSON.toJSONString(r));
        return Result.success(r);
    }

    @Override
    public Result<AllocateOrderCreateResponse> createAllocateOrderApi(AllocateOrderCreateRequest allocateOrderCreateRequest) {
        log.info("createAllocateOrderApi_request:{}",JSON.toJSONString(allocateOrderCreateRequest));
        // 创建
        String id = allocateOrderWriteRepository.createAllocateOrderApi(allocateOrderCreateRequest);
        AllocateOrderRequest allocateOrderRequest = new AllocateOrderRequest();
        allocateOrderRequest.setAllocateOrderId(id);
        AllocateOrderWithOrderDetailsSDO freshSdo = allocateOrderReadRepository.get(allocateOrderRequest);
        AllocateOrderSDO allocateOrderSDO = freshSdo.getAllocateOrderSDO();

        if(ObjectUtil.isEmpty(allocateOrderCreateRequest.getSubmitFlag())){
            log.error("syncAllocateOrderToSAPError,createApi,submitFlag,allocateOrderCreateRequest:{}" + JSON.toJSONString(allocateOrderCreateRequest));
            throw new FacadeException("submitFlag is null");
        }

        // 构建返回结果
        AllocateOrderCreateResponse response = new AllocateOrderCreateResponse();
        response.setId(id);
        response.setCode(allocateOrderSDO.getCode());
        response.setAllocateOrderDetailList(freshSdo.getAllocateOrderDetailSDOS());
        response.setThirdAllocateOrderId(allocateOrderCreateRequest.getThirdAllocateOrderId());
        // 记录日志
        saveDocumentLog(new AllocateOrderWithOrderDetailsSDO(),freshSdo, AllocateApplyOrderOperationTypeEnum.CREATE, OperatorType.USER.getOperatorType(),null);

        return Result.success(response);
    }

    /**
     * 不用了
     * 调拨单同步 基本数据创建 sap->共享平台 F-F1 创建也包含了更新  008
     * 场景 F-F1
     * @param allocateOrderSyncRequest
     * @return
     */
    @Override
    @FacadeInvoker
    @Deprecated
    public Result<AllocateOrderSyncResponse> sync(AllocateOrderSyncRequest allocateOrderSyncRequest) {
        AllocateOrderSyncResponse r = new AllocateOrderSyncResponse();
        return Result.success(r);
    }

    /**
     * 保存/更新 草稿
     * @param request
     * @return
     */
    @Override
    @FacadeInvoker(value = "调拨单保存",errorCode = "OTS-03-005-00-15-049")
    public Result<AllocateOrderSaveResponse> save(AllocateOrderSaveRequest request) {
        log.info("AllocateOrderWriteServiceImpl.save 调拨单保存:" + request==null?"":JSON.toJSONString(request));

        if (request == null || CollectionUtils.isEmpty(request.getAllocateOrderScItemDetails())){
            throw new FacadeException("OTS-03-005-00-15-058");
//            return Result.fail("allocateOrderSaveError","必须添加货品明细");
        }
        AllocateOrderRequest allocateOrderRequest = new AllocateOrderRequest();
        allocateOrderRequest.setAllocateOrderCode(request.getCode());
        AllocateOrderWithOrderDetailsSDO sdo = allocateOrderReadRepository.get(allocateOrderRequest);
        String save = allocateOrderWriteRepository.save(request);
        AllocateOrderWithOrderDetailsSDO fresh = allocateOrderReadRepository.get(allocateOrderRequest);
        saveDocumentLog(sdo,fresh,AllocateApplyOrderOperationTypeEnum.UPDATE,
                OperatorType.USER.getOperatorType(),null);
        AllocateOrderSaveResponse response = new AllocateOrderSaveResponse();
        response.setCode(save);
        log.info("AllocateOrderWriteServiceImpl.save 调拨单保存:" + JSON.toJSONString(response));
        return Result.success(response);
    }

    /**
     * 调拨单修改
     * @param request req
     * @return allocate
     */
    @Override
    @FacadeInvoker(value = "调拨单保存",errorCode = "OTS-03-005-00-15-049")
    public Result<AllocateOrderSaveResponse> saveUpdateAllocateOrder(AllocateOrderSaveRequest request) {
        log.info("AllocateOrderWriteServiceImpl.saveUpdateAllocateOrder 调拨单保存:{}",JSON.toJSONString(request));

        if (request == null || CollectionUtils.isEmpty(request.getAllocateOrderScItemDetails())){
            throw new FacadeException("OTS-03-005-00-15-058");
//            return Result.fail("allocateOrderSaveError","必须添加货品明细");
        }

        AllocateOrderRequest allocateOrderRequest = new AllocateOrderRequest();
        allocateOrderRequest.setAllocateOrderCode(request.getCode());
        AllocateOrderWithOrderDetailsSDO sdo = allocateOrderReadRepository.get(allocateOrderRequest);
        String save = allocateOrderWriteRepository.saveUpdate(request);
        AllocateOrderWithOrderDetailsSDO fresh = allocateOrderReadRepository.get(allocateOrderRequest);
        saveDocumentLog(sdo,fresh,AllocateApplyOrderOperationTypeEnum.UPDATE,
                OperatorType.USER.getOperatorType(),null);
        AllocateOrderSaveResponse response = new AllocateOrderSaveResponse();
        response.setCode(save);
        log.info("AllocateOrderWriteServiceImpl.saveUpdateAllocateOrder 调拨单保存:" + JSON.toJSONString(response));
        return Result.success(response);
    }

    @Override
    @FacadeInvoker(errorCode = "OTS-03-005-00-15-049")
    public Result<AllocateOrderCreateResponse> updateSaveAllocateOrder(AllocateOrderSaveRequest request) {
        log.info("AllocateOrderWriteServiceImpl.saveUpdateAllocateOrder 调拨单保存:{}",JSON.toJSONString(request));

        if (request == null || CollectionUtils.isEmpty(request.getAllocateOrderScItemDetails())){
            throw new FacadeException("OTS-03-005-00-15-058");
        }

        AllocateOrderRequest allocateOrderRequest = new AllocateOrderRequest();
        allocateOrderRequest.setAllocateOrderId(request.getId());
        AllocateOrderWithOrderDetailsSDO sdo = allocateOrderReadRepository.get(allocateOrderRequest);
        AllocateOrderCreateResponse response = allocateOrderWriteRepository.updateAllocateOrder(request);
        AllocateOrderWithOrderDetailsSDO fresh = allocateOrderReadRepository.get(allocateOrderRequest);
        AllocateOrderWithOrderDetailsSDO message = fresh;
        saveDocumentLog(sdo,fresh,AllocateApplyOrderOperationTypeEnum.UPDATE,
                OperatorType.USER.getOperatorType(),null);
        log.info("AllocateOrderWriteServiceImpl.saveUpdateAllocateOrder 调拨单保存:" + JSON.toJSONString(response));
        Map<String,String> map = Maps.newHashMap();
        map.put("_CZ_KEY", BcOrderConstants.TENANT_ID());
        try {
            message.getAllocateOrderSDO().setUpdater(request.getOperatorId());
            boolean sendResult = epochMessageService.sendMessage("update_allocate_order_message","CZ_ALLOCATE_V2","ALLOCATE_ORDER_UPDATE",response.getCode(), JSON.toJSONString(message), map);
            log.info("AllocateOrderWriteServiceImpl.saveUpdateAllocateOrder 调拨单更新,sendMessage:" + sendResult);
        } catch (Exception e) {
            log.error("AllocateOrderWriteServiceImpl.saveUpdateAllocateOrder 调拨单更新,sendMessage|error",e);
        }

        return Result.success(response);
    }
    /**
     * 提交调拨单 todo FacadeInvoker信息补全 @余红伟
     * @param allocateOrderSubmitRequest
     * @return
     */
    @Override
    @FacadeInvoker(value = "提交调拨单",errorCode = "OTS-03-005-00-15-048")
    public Result<AllocateOrderSubmitResponse> submit(AllocateOrderSubmitRequest allocateOrderSubmitRequest) {
        log.info("AllocateOrderWriteServiceImpl.submit 提交调拨单:" + allocateOrderSubmitRequest==null?"":JSON.toJSONString(allocateOrderSubmitRequest));

        AllocateOrderRequest allocateOrderRequest = new AllocateOrderRequest();
        allocateOrderRequest.setAllocateOrderCode(allocateOrderSubmitRequest.getCode());
        AllocateOrderWithOrderDetailsSDO sdo = allocateOrderReadRepository.get(allocateOrderRequest);
        String submit = allocateOrderWriteRepository.submit(allocateOrderSubmitRequest);
        AllocateOrderWithOrderDetailsSDO fresh = allocateOrderReadRepository.get(allocateOrderRequest);
        saveDocumentLog(sdo,fresh,AllocateApplyOrderOperationTypeEnum.SUBMIT,
                OperatorType.USER.getOperatorType(),null);
        AllocateOrderSubmitResponse response = new AllocateOrderSubmitResponse();
        response.setCode(submit);
        try{
            if (StringUtil.isNotEmpty(submit)){
                Integer type = sdo.getAllocateOrderSDO() == null? null:sdo.getAllocateOrderSDO().getType();
                Long id = sdo.getAllocateOrderSDO() == null? null:sdo.getAllocateOrderSDO().getId();
                //如果是总部销售的 走012接口; 非总部的走009接口重新推送
                if (Objects.equals(AllocateOrderTypeEnum.HEADQUARTER_SALES.getType(),type)){
                    AllocateOrderSalesOrderCreateSAPRequest createSAPRequest = new AllocateOrderSalesOrderCreateSAPRequest();
                    createSAPRequest.setCode(submit);
                    createSalesOrderSAP(createSAPRequest);
                }else {
                    AllocateOrderSubmitSAPRequest submitSAPRequest = new AllocateOrderSubmitSAPRequest();
                    submitSAPRequest.setId(id == null?null:id.toString());
                    submitSAP(submitSAPRequest);
                }
//                AllocateOrderDetailsQueryRequest queryRequest = new AllocateOrderDetailsQueryRequest();
//                queryRequest.setCode(submit);
//                Result<AllocateOrderDetailsQueryResponse> query = allocateOrderReadService.query(queryRequest);
                AllocateOrderGetMessageRequest allocateOrderGetMessageRequest = new AllocateOrderGetMessageRequest();
                allocateOrderGetMessageRequest.setCode(submit);
                Result<AllocateOrderMessageResponse> allocateOrderMessage = allocateOrderReadService.getAllocateOrderMessage(allocateOrderGetMessageRequest);
                Map<String,String> map = Maps.newHashMap();
                map.put("_CZ_KEY", BcOrderConstants.TENANT_ID());
                boolean f = epochMessageService.sendMessage("allocate_order_message","CZ_ALLOCATE_V2","AllocateCreate",submit, JSON.toJSONString(allocateOrderMessage.getResult()),
                        map);
            }
        }catch(ErrorCodeException e){
            log.error(e,"AllocateOrderWriteServiceImpl.submit exception,code:{},msg:{}",e.getErrorCode(),e.getErrorMessage());
            return Result.fail(e.getErrorCode(), e.getErrorMessage());
        }catch (Throwable e){
            log.error("submitAllocateOrderError, allocateOrderId:" + submit, e);
            throw e;
        }
        log.info("AllocateOrderWriteServiceImpl.submit 提交调拨单.result:" + JSON.toJSONString(response));
        return Result.success(response);
    }

    /**
     * 提交调拨单 (不校验)
     * @param allocateOrderSubmitRequest
     * @return
     */
    @Override
    @FacadeInvoker(value = "提交调拨单 不校验",errorCode = "OTS-03-005-00-15-048")
    public Result<AllocateOrderSubmitResponse> submitNotCheck(AllocateOrderSubmitRequest allocateOrderSubmitRequest) {
        log.info("AllocateOrderWriteServiceImpl.submit 提交调拨单:" + allocateOrderSubmitRequest==null?"":JSON.toJSONString(allocateOrderSubmitRequest));

        AllocateOrderRequest allocateOrderRequest = new AllocateOrderRequest();
        allocateOrderRequest.setAllocateOrderCode(allocateOrderSubmitRequest.getCode());
        AllocateOrderWithOrderDetailsSDO sdo = allocateOrderReadRepository.get(allocateOrderRequest);
        //String submit = allocateOrderWriteRepository.submit(allocateOrderSubmitRequest);
        String submit = allocateOrderWriteRepository.submitNotCheck(allocateOrderSubmitRequest);
        AllocateOrderWithOrderDetailsSDO fresh = allocateOrderReadRepository.get(allocateOrderRequest);
        saveDocumentLog(sdo,fresh,AllocateApplyOrderOperationTypeEnum.SUBMIT,
                OperatorType.USER.getOperatorType(),null);
        AllocateOrderSubmitResponse response = new AllocateOrderSubmitResponse();
        response.setCode(submit);
        try{
            if (StringUtil.isNotEmpty(submit)){
                Integer type = sdo.getAllocateOrderSDO() == null? null:sdo.getAllocateOrderSDO().getType();
                Long id = sdo.getAllocateOrderSDO() == null? null:sdo.getAllocateOrderSDO().getId();
                //如果是总部销售的 走012接口; 非总部的走009接口重新推送
                if (Objects.equals(AllocateOrderTypeEnum.HEADQUARTER_SALES.getType(),type)){
                    AllocateOrderSalesOrderCreateSAPRequest createSAPRequest = new AllocateOrderSalesOrderCreateSAPRequest();
                    createSAPRequest.setCode(submit);
                    createSalesOrderSAP(createSAPRequest);
                }else {
                    AllocateOrderSubmitSAPRequest submitSAPRequest = new AllocateOrderSubmitSAPRequest();
                    submitSAPRequest.setId(id == null?null:id.toString());
                    submitSAP(submitSAPRequest);
                }
//                AllocateOrderDetailsQueryRequest queryRequest = new AllocateOrderDetailsQueryRequest();
//                queryRequest.setCode(submit);
//                Result<AllocateOrderDetailsQueryResponse> query = allocateOrderReadService.query(queryRequest);
                AllocateOrderGetMessageRequest allocateOrderGetMessageRequest = new AllocateOrderGetMessageRequest();
                allocateOrderGetMessageRequest.setCode(submit);
                Result<AllocateOrderMessageResponse> allocateOrderMessage = allocateOrderReadService.getAllocateOrderMessage(allocateOrderGetMessageRequest);
                Map<String,String> map = Maps.newHashMap();
                map.put("_CZ_KEY", BcOrderConstants.TENANT_ID());
                boolean f = epochMessageService.sendMessage("allocate_order_message","CZ_ALLOCATE_V2","AllocateCreate",submit, JSON.toJSONString(allocateOrderMessage.getResult()),
                        map);
            }
        }catch(ErrorCodeException e){
            log.error(e,"AllocateOrderWriteServiceImpl.submit exception,code:{},msg:{}",e.getErrorCode(),e.getErrorMessage());
            return Result.fail(e.getErrorCode(), e.getErrorMessage());
        }catch (Throwable e){
            log.error("submitAllocateOrderError, allocateOrderId:" + submit, e);
            throw e;
        }
        log.info("AllocateOrderWriteServiceImpl.submit 提交调拨单.result:" + JSON.toJSONString(response));
        return Result.success(response);
    }

    @Override
    public Result<AllocateOrderWithdrawResponse> withdraw(AllocateOrderWithdrawRequest allocateOrderWithdrawRequest) {
        return null;
    }

    @Override
    public Result<AllocateOrderUpdateResponse> updateAllocateOrder(AllocateOrderUpdateRequest allocateOrderUpdateRequest) {
        return null;
    }

    @Override
    @FacadeInvoker(value = "调拨单取消",errorCode = "OTS-03-005-00-15-047")
    public Result<AllocateOrderCancelResponse> cancel(AllocateOrderCancelRequest allocateOrderCancelRequest) {
        log.info("AllocateOrderWriteServiceImpl.cancel 调拨单取消:" + allocateOrderCancelRequest==null?"":JSON.toJSONString(allocateOrderCancelRequest));

        AllocateOrderRequest allocateOrderRequest = new AllocateOrderRequest();
        allocateOrderRequest.setAllocateOrderCode(allocateOrderCancelRequest.getCode());
        AllocateOrderWithOrderDetailsSDO sdo = allocateOrderReadRepository.get(allocateOrderRequest);
        AllocateOrderCancelResponse response = allocateOrderWriteRepository.cancel(allocateOrderCancelRequest);
        AllocateOrderWithOrderDetailsSDO fresh = allocateOrderReadRepository.get(allocateOrderRequest);
        saveDocumentLog(sdo,fresh,
                AllocateApplyOrderOperationTypeEnum.CANCEL,
                OperatorType.USER.getOperatorType(),null);
        log.info("AllocateOrderWriteServiceImpl.cancel 调拨单取消 result:" + JSON.toJSONString(response));
        return Result.success(response);
    }

    /**
     * 发货
     * @param allocateOrderShippedRequest
     * @return
     */
    @Override
    @FacadeInvoker(value = "调拨单发货", errorCode = "OTS-03-005-00-15-042")
    public Result<AllocateOrderShippedResponse> shipped(AllocateOrderShippedRequest allocateOrderShippedRequest) {
        log.info("materialHandle_shipped_request,material:{}, request:{}", allocateOrderShippedRequest.getOutMaterialCertificateId(), JSON.toJSONString(allocateOrderShippedRequest));
        AllocateOrderRequest allocateOrderRequest = new AllocateOrderRequest();
        allocateOrderRequest.setAllocateOrderId(allocateOrderShippedRequest.getId());
        AllocateOrderWithOrderDetailsSDO sdo = allocateOrderReadRepository.get(allocateOrderRequest);
        log.info("AllocateOrderWriteServiceImpl.shipped.before:{}",JSON.toJSONString(sdo));
        if (StringUtils.isEmpty(getOutAllocateOrderId(sdo))) {
            log.error("AllocateOrderWriteServiceImpl.shipped.outAllocateOrderId.isEmpty");
            throw new FacadeException("OTS-03-005-00-15-042");
        }
        AllocateOrderShippedResponse response = new AllocateOrderShippedResponse();
        try(RedisLock lock = new RedisLock(BcOrderConstants.REDIS_CACHE_CODE(),ALLOCATE_ORDER_LOCK_SYNC + getOutAllocateOrderId(sdo),5 * 60 * 1000,10 * 60 * 1000)){
            if (!lock.lock()){
                throw new FacadeException("OTS-03-005-00-15-096");
            }
            response = allocateOrderWriteRepository.shipped(allocateOrderShippedRequest);
        }catch (Exception e){
            log.error("allocateOrderShippedError",e);
            throw new FacadeException("OTS-03-005-00-15-042");
        }
        AllocateOrderWithOrderDetailsSDO fresh = allocateOrderReadRepository.get(allocateOrderRequest);
        log.info("AllocateOrderWriteServiceImpl.shipped.after:{}",JSON.toJSONString(fresh));
        saveDocumentLog(sdo,fresh, AllocateApplyOrderOperationTypeEnum.Shipped,
                OperatorType.INNER_INTERFACE.getOperatorType(),allocateOrderShippedRequest.getSendTime());
        log.info("materialHandle_shipped_response,material:{}, response:{}", allocateOrderShippedRequest.getOutMaterialCertificateId(), JSON.toJSONString(response));
        return Result.success(response);
    }

    @Override
    @FacadeInvoker(value = "调拨单收货", errorCode = "OTS-03-005-00-15-043")
    public Result<AllocateOrderReceivedResponse> received(AllocateOrderReceivedRequest allocateOrderReceivedRequest) {
        log.info("materialHandle_received_request,material:{}, request:{}", allocateOrderReceivedRequest.getOutMaterialCertificateId(), JSON.toJSONString(allocateOrderReceivedRequest));
        AllocateOrderRequest allocateOrderRequest = new AllocateOrderRequest();
        allocateOrderRequest.setAllocateOrderId(allocateOrderReceivedRequest.getId());
        AllocateOrderWithOrderDetailsSDO sdo = allocateOrderReadRepository.get(allocateOrderRequest);
        log.info("AllocateOrderWriteServiceImpl.received.before:{}",JSON.toJSONString(sdo));
        if (StringUtils.isEmpty(getOutAllocateOrderId(sdo))) {
            log.error("AllocateOrderWriteServiceImpl.received.outAllocateOrderId.isEmpty");
            throw new FacadeException("OTS-03-005-00-15-043");
        }
        AllocateOrderReceivedResponse response = new AllocateOrderReceivedResponse();
        try(RedisLock lock = new RedisLock(BcOrderConstants.REDIS_CACHE_CODE(),ALLOCATE_ORDER_LOCK_SYNC + getOutAllocateOrderId(sdo),5 * 60 * 1000,10 * 60 * 1000)){
            if (!lock.lock()){
                throw new FacadeException("OTS-03-005-00-15-096");
            }
            response = allocateOrderWriteRepository.received(allocateOrderReceivedRequest);
        }catch (Exception e){
            log.error("allocateOrderReceivedError",e);
            throw new FacadeException("OTS-03-005-00-15-043");
        }
//        AllocateOrderReceivedResponse response = allocateOrderWriteRepository.received(allocateOrderReceivedRequest);
        AllocateOrderWithOrderDetailsSDO fresh = allocateOrderReadRepository.get(allocateOrderRequest);
        log.info("AllocateOrderWriteServiceImpl.received.after:{}",JSON.toJSONString(fresh));
        saveDocumentLog(sdo,fresh,AllocateApplyOrderOperationTypeEnum.RECEIVED,
                OperatorType.INNER_INTERFACE.getOperatorType(),allocateOrderReceivedRequest.getSendTime());
        log.info("materialHandle_received_response,material:{}, response:{}", allocateOrderReceivedRequest.getOutMaterialCertificateId(), JSON.toJSONString(response));
        return Result.success(response);
    }

    @Override
    public Result<AllocateOrderResponse> backward(AllocateOrderBackwardDTO allocateOrderBackwardDTO) {
        return null;
    }

    /**
     * 销售订单更新
     * @param request
     * @return
     */
    @Override
    @FacadeInvoker
    @Deprecated
    public Result<AllocateOrderSalesOrderUpdateResponse> salesOrderUpdate(AllocateOrderSalesOrderUpdateRequest request) {
        log.info("AllocateOrderWriteServiceImpl.salesOrderUpdate :" + request==null?"":JSON.toJSONString(request));

        AllocateOrderEntity entity = AllocateOrderFactory.salesOrderUpdate(request);
        String id = entity.salesOrderUpdate();
        AllocateOrderSalesOrderUpdateResponse response = new AllocateOrderSalesOrderUpdateResponse();
        response.setCode(id);
        return null;
    }

    /**
     * 008 同步更新调拨单
     * @param allocateOrderSyncRequest
     * @return
     */
    @Override
    @FacadeInvoker(value = "008同步调拨单",errorCode = "OTS-03-005-00-15-044")
    public Result<AllocateOrderSyncResponse> passiveSync(AllocateOrderSyncRequest allocateOrderSyncRequest) {
        log.info("sap008接口入参："+ JSONObject.toJSONString(allocateOrderSyncRequest));

        AllocateOrderRequest allocateOrderRequest = new AllocateOrderRequest();
        AllocateOrderWithOrderDetailsSDO sdoOld = new AllocateOrderWithOrderDetailsSDO();
        AllocateOrderSyncResponse allocateOrderSyncResponse = null;
        Result<AllocateApplyOrderDetailDTO> allocateApplyOrderDetailDTOOld = new Result<>();
        try(RedisLock lock = new RedisLock(CrUnifiedInventoryConstants.REDIS_CACHE_CODE(),ALLOCATE_ORDER_LOCK_SYNC + allocateOrderSyncRequest.getOutAllocateOrderId(),5 * 60 * 1000,10 * 60 * 1000)){

            if (!lock.lock()){
                throw new FacadeException("OTS-03-005-00-15-096");
            }

            if (Objects.nonNull(allocateOrderSyncRequest.getAllocateApplyOrderCode())) {
                QueryAllocateApplyOrderRequest queryAllocateApplyOrderRequest = new QueryAllocateApplyOrderRequest();
                queryAllocateApplyOrderRequest.setCode(allocateOrderSyncRequest.getAllocateApplyOrderCode());
                allocateApplyOrderDetailDTOOld = allocateApplyOrderReadService.queryAllocateApplyOrderByCode(queryAllocateApplyOrderRequest);
            }
            if (StringUtils.isNotEmpty(allocateOrderSyncRequest.getCode())||StringUtils.isNotEmpty(allocateOrderSyncRequest.getOutAllocateOrderId())) {
                allocateOrderRequest.setAllocateOrderCode(allocateOrderSyncRequest.getCode());
                allocateOrderRequest.setOutAllocateOrderId(allocateOrderSyncRequest.getOutAllocateOrderId());
                sdoOld = allocateOrderReadRepository.get(allocateOrderRequest);
            }
            //判断时间戳 如果时间
            String modifierTime = epochCacheService.get(BcOrderConstants.REDIS_CACHE_CODE(),
                    ALLOCATE_ORDER_CONFIRM_QUANTITY_KEY + allocateOrderSyncRequest.getOutAllocateOrderId());
            //下发时间戳
            String ext = allocateOrderSyncRequest.getExt();
            if (StringUtils.isNotBlank(modifierTime)){
                String[] modifierTimeArray = modifierTime.split("\\.");
                String[] extArray = ext.split("\\.");
                if (Long.parseLong(modifierTimeArray[0]) > Long.parseLong(extArray[0])){
                    AllocateOrderSyncResponse response = new AllocateOrderSyncResponse();
                    response.setCode("当前修改时间早于上一次 不允许更新");
                    return Result.success(response);
                }else if (Long.valueOf(modifierTimeArray[0]).longValue() == Long.valueOf(extArray[0]).longValue()) {
                    if (Long.parseLong(modifierTimeArray[1]) >= Long.parseLong(extArray[1])){
                        AllocateOrderSyncResponse response = new AllocateOrderSyncResponse();
                        response.setCode("当前修改时间早于上一次 不允许更新");
                        return Result.success(response);
                    }
                }
            }
            //设置缓存
//            epochCacheService.put(BcOrderConstants.REDIS_CACHE_CODE(),
//                    ALLOCATE_ORDER_CONFIRM_QUANTITY_KEY + allocateOrderSyncRequest.getOutAllocateOrderId(),
//                    ext, SAP_TIMESTAMP_CACHE_EXPIRE, TimeUnit.SECONDS);
            allocateOrderSyncResponse = allocateOrderWriteRepository.passiveSync(allocateOrderSyncRequest);
            //008执行成功了 记录缓存时间戳
            if (Objects.nonNull(allocateOrderSyncResponse) && StringUtils.isNotBlank(allocateOrderSyncResponse.getCode())
                    && allocateOrderSyncResponse.getCode().startsWith("STO")){
                epochCacheService.put(BcOrderConstants.REDIS_CACHE_CODE(),
                        ALLOCATE_ORDER_CONFIRM_QUANTITY_KEY + allocateOrderSyncRequest.getOutAllocateOrderId(),
                        ext, SAP_TIMESTAMP_CACHE_EXPIRE, TimeUnit.SECONDS);
            }
        }catch (Exception e){
            log.error("passiveSyncAllocateOrderError",e);
            throw new FacadeException("OTS-03-005-00-15-097");
        }

        if (StringUtils.isEmpty(allocateOrderSyncRequest.getCode())) {
            allocateOrderRequest.setAllocateOrderCode(allocateOrderSyncResponse.getCode());
        }
        AllocateOrderWithOrderDetailsSDO sdoNew = allocateOrderReadRepository.get(allocateOrderRequest);
        if (StringUtils.isNotEmpty(allocateOrderSyncRequest.getCode())) {
            log.info("008接口记录日志更新方法：旧值：{}，新值:{}",JSONObject.toJSONString(sdoOld),JSONObject.toJSONString(sdoNew));
            if (allocateOrderSyncResponse != null && StringUtils.isNotBlank(allocateOrderSyncResponse.getCode())
                    && allocateOrderSyncResponse.getCode().startsWith("STO")){
                saveDocumentLog(sdoOld,sdoNew==null?sdoOld:sdoNew, AllocateApplyOrderOperationTypeEnum.CHANGE, OperatorType.USER.getOperatorType(),null);
            }
        }else{
            log.info("008接口记录日志创建方法：旧值：{}，新值:{}",JSONObject.toJSONString(sdoOld),JSONObject.toJSONString(sdoNew));
            if (allocateOrderSyncResponse != null && StringUtils.isNotBlank(allocateOrderSyncResponse.getCode())
                    && allocateOrderSyncResponse.getCode().startsWith("STO")){
                saveDocumentLog(sdoOld,sdoNew==null?sdoOld:sdoNew, AllocateApplyOrderOperationTypeEnum.CREATE, OperatorType.USER.getOperatorType(),null);
            }
        }

        Result<AllocateApplyOrderDetailDTO> allocateApplyOrderDetailDTONew = new Result<>();
        if (StringUtils.isNotBlank(allocateOrderSyncRequest.getAllocateApplyOrderCode())) {
            QueryAllocateApplyOrderRequest queryAllocateApplyOrderRequest = new QueryAllocateApplyOrderRequest();
            queryAllocateApplyOrderRequest.setCode(allocateOrderSyncRequest.getAllocateApplyOrderCode());
            allocateApplyOrderDetailDTONew = allocateApplyOrderReadService.queryAllocateApplyOrderByCode(queryAllocateApplyOrderRequest);
        }
        //添加调拨预报单单据日志
        if (StringUtils.isNotBlank(allocateOrderSyncRequest.getAllocateApplyOrderCode())) {
            Map<String, String> features = new HashMap<>();
            features.put(CODE, allocateOrderSyncRequest.getAllocateApplyOrderCode());
            if (allocateApplyOrderDetailDTONew.getResult() != null &&
                    allocateApplyOrderDetailDTONew.getResult().getStatus() != null){
                features.put(STATUS, allocateApplyOrderDetailDTONew.getResult().getStatus().toString());
            }
//            features.put(STATUS, allocateApplyOrderDetailDTONew.getResult().getStatus().toString());
            AllocateApplyOrderDetailDTO allocateApplyOrderDetailOld = allocateApplyOrderDetailDTOOld.getResult();
            AllocateApplyOrderDetailDTO allocateApplyOrderDetailNew = allocateApplyOrderDetailDTONew.getResult();
            saveAllocateApplyOrderDocumentLog(allocateApplyOrderDetailOld, allocateApplyOrderDetailNew, Integer.valueOf(AllocateApplyOrderOperationTypeEnum.UPDATE.getValue()), features);
        }
        try{
            if (allocateOrderSyncResponse != null && StringUtils.isNotBlank(allocateOrderSyncResponse.getCode())
                    && allocateOrderSyncResponse.getCode().startsWith("STO")){
//                AllocateOrderDetailsQueryRequest queryRequest = new AllocateOrderDetailsQueryRequest();
//                queryRequest.setCode(allocateOrderSyncResponse.getCode());
//                Result<AllocateOrderDetailsQueryResponse> query = allocateOrderReadService.query(queryRequest);
                AllocateOrderGetMessageRequest allocateOrderGetMessageRequest = new AllocateOrderGetMessageRequest();
                allocateOrderGetMessageRequest.setCode(allocateOrderSyncResponse.getCode());
                Result<AllocateOrderMessageResponse> allocateOrderMessage = allocateOrderReadService.getAllocateOrderMessage(allocateOrderGetMessageRequest);
                Map<String,String> map = Maps.newHashMap();
                map.put("_CZ_KEY", BcOrderConstants.TENANT_ID());
                epochMessageService.sendMessage("allocate_order_message","CZ_ALLOCATE_V2","AllocateCreate",
                        allocateOrderSyncResponse.getCode(),JSON.toJSONString(allocateOrderMessage.getResult()),map);
            }
        }catch(ErrorCodeException e){
            log.error(e,"AllocateOrderWriteServiceImpl.passiveSync exception,code:{},msg:{}",e.getErrorCode(),e.getErrorMessage());
            return Result.fail(e.getErrorCode(), e.getErrorMessage());
        }catch (Throwable e){
            log.error("AllocateOrderWriteServiceImpl.passiveSync exception:",e);
            // 异常在FacadeInvokeHandler中会处理
            throw e;
        }
        return Result.success(allocateOrderSyncResponse);
    }


    @FacadeInvoker(value = "008同步调拨单",errorCode = "OTS-03-005-00-15-044")
    public Result<AllocateOrderSyncResponse> passiveSyncApi(AllocateOrderSyncRequest allocateOrderSyncRequest) {
        log.info("sap008.passiveSyncApi{}", JSONObject.toJSONString(allocateOrderSyncRequest));

        AllocateOrderRequest allocateOrderRequest = new AllocateOrderRequest();
        AllocateOrderWithOrderDetailsSDO sdoOld = new AllocateOrderWithOrderDetailsSDO();
        AllocateOrderSyncResponse allocateOrderSyncResponse = null;
        String scItemIds = allocateOrderSyncRequest.getAllocateOrderScItemDetails()
                .stream().map(AllocateOrderScItemDetailDTO::getScItemId).collect(Collectors.joining("_"));
        try{

//            if (!lock.lock()){
//                throw new FacadeException("OTS-03-005-00-15-096");
//            }
            if (StringUtils.isNotEmpty(allocateOrderSyncRequest.getCode())||StringUtils.isNotEmpty(allocateOrderSyncRequest.getOutAllocateOrderId())) {
                allocateOrderRequest.setAllocateOrderCode(allocateOrderSyncRequest.getCode());
                allocateOrderRequest.setOutAllocateOrderId(allocateOrderSyncRequest.getOutAllocateOrderId());
                sdoOld = allocateOrderReadRepository.get(allocateOrderRequest);
            }
            //判断时间戳 如果时间
//            String modifierTime = epochCacheService.get(BcOrderConstants.REDIS_CACHE_CODE(),
//                    ALLOCATE_ORDER_CONFIRM_QUANTITY_KEY + allocateOrderSyncRequest.getOutAllocateOrderId());
            //下发时间戳
//            String ext = allocateOrderSyncRequest.getExt();
//            if (StringUtils.isNotBlank(modifierTime)){
//                String[] modifierTimeArray = modifierTime.split("\\.");
//                String[] extArray = ext.split("\\.");
//                if (Long.parseLong(modifierTimeArray[0]) > Long.parseLong(extArray[0])){
//                    AllocateOrderSyncResponse response = new AllocateOrderSyncResponse();
//                    response.setCode("当前修改时间早于上一次 不允许更新");
//                    return Result.success(response);
//                }else if (Long.valueOf(modifierTimeArray[0]).longValue() == Long.valueOf(extArray[0]).longValue()) {
//                    if (Long.parseLong(modifierTimeArray[1]) >= Long.parseLong(extArray[1])){
//                        AllocateOrderSyncResponse response = new AllocateOrderSyncResponse();
//                        response.setCode("当前修改时间早于上一次 不允许更新");
//                        return Result.success(response);
//                    }
//                }
//            }

            allocateOrderSyncResponse = allocateOrderWriteRepository.passiveSyncApi(allocateOrderSyncRequest);
        }catch (Exception e){
            log.error("passiveSyncAllocateOrderError.passiveSyncApi",e);
            throw new FacadeException("OTS-03-005-00-15-097");
        }

        if (StringUtils.isEmpty(allocateOrderSyncRequest.getCode())) {
            allocateOrderRequest.setAllocateOrderCode(allocateOrderSyncResponse.getCode());
        }
        AllocateOrderWithOrderDetailsSDO sdoNew = allocateOrderReadRepository.get(allocateOrderRequest);
        if(allocateOrderSyncResponse !=null){
            allocateOrderSyncResponse.setAllocateOrderDetailList(sdoNew.getAllocateOrderDetailSDOS());
        }
        if (StringUtils.isNotEmpty(allocateOrderSyncRequest.getCode())) {
            log.info("passiveSyncApi.allocateOrderCode.008接口记录日志更新方法：旧值：{}，新值:{}",JSONObject.toJSONString(sdoOld),JSONObject.toJSONString(sdoNew));
            if (allocateOrderSyncResponse != null && StringUtils.isNotBlank(allocateOrderSyncResponse.getCode())
                    && allocateOrderSyncResponse.getCode().startsWith("STO")){
                saveDocumentLog(sdoOld,sdoNew==null?sdoOld:sdoNew, AllocateApplyOrderOperationTypeEnum.CHANGE, OperatorType.USER.getOperatorType(),null);
            }
        }else{
            log.info("passiveSyncApi.allocateOrderCode.008接口记录日志创建方法：旧值：{}，新值:{}",JSONObject.toJSONString(sdoOld),JSONObject.toJSONString(sdoNew));
            if (allocateOrderSyncResponse != null && StringUtils.isNotBlank(allocateOrderSyncResponse.getCode())
                    && allocateOrderSyncResponse.getCode().startsWith("STO")){
                saveDocumentLog(sdoOld,sdoNew==null?sdoOld:sdoNew, AllocateApplyOrderOperationTypeEnum.CREATE, OperatorType.USER.getOperatorType(),null);
            }
        }
        return Result.success(allocateOrderSyncResponse);
    }
    /**
     * 创建退单 -- 传入发票后生成对应的调拨单
     * @param allocateOrderReturnOrderRequest
     * @return
     */
    @Override
    @FacadeInvoker(value = "创建退单",errorCode = "OTS-03-005-00-15-045")
    public Result<AllocateOrderReturnOrderResponse> returnOrder(AllocateOrderReturnOrderRequest allocateOrderReturnOrderRequest) {
        log.info("AllocateOrderWriteServiceImpl.returnOrder :" + allocateOrderReturnOrderRequest==null?"":JSON.toJSONString(allocateOrderReturnOrderRequest));


        AllocateOrderReturnOrderResponse response = allocateOrderWriteRepository.returnOrder(allocateOrderReturnOrderRequest);
        AllocateOrderRequest allocateOrderRequest = new AllocateOrderRequest();
        allocateOrderRequest.setAllocateOrderCode(response.getCode());
        AllocateOrderWithOrderDetailsSDO sdo = allocateOrderReadRepository.get(allocateOrderRequest);
        saveDocumentLog(new AllocateOrderWithOrderDetailsSDO(),sdo,
                AllocateApplyOrderOperationTypeEnum.CREATE,
                OperatorType.USER.getOperatorType(),null);
        if (response != null && StringUtils.isNotBlank(response.getCode())){
            String submit = response.getCode();
            try{
                if (StringUtil.isNotEmpty(submit)){
                    AllocateOrderSalesOrderCreateSAPRequest createSAPRequest = new AllocateOrderSalesOrderCreateSAPRequest();
                    createSAPRequest.setCode(submit);
                    createSalesOrderSAP(createSAPRequest);
                    AllocateOrderGetMessageRequest allocateOrderGetMessageRequest = new AllocateOrderGetMessageRequest();
                    allocateOrderGetMessageRequest.setCode(submit);
                    Result<AllocateOrderMessageResponse> allocateOrderMessage = allocateOrderReadService.getAllocateOrderMessage(allocateOrderGetMessageRequest);
                    Map<String,String> map = Maps.newHashMap();
                    map.put("_CZ_KEY", BcOrderConstants.TENANT_ID());
                    epochMessageService.sendMessage("allocate_order_message","CZ_ALLOCATE_V2","AllocateCreate",
                            submit,JSON.toJSONString(allocateOrderMessage.getResult()),map);
                }
            }catch(ErrorCodeException e){
                log.error(e,"AllocateOrderWriteServiceImpl.passiveSync exception,code:{},msg:{}",e.getErrorCode(),e.getErrorMessage());
                return Result.fail(e.getErrorCode(), e.getErrorMessage());
            }catch (Throwable e) {
                log.error("submitAllocateOrderError, allocateOrderId:" + submit, e);
                // 异常在FacadeInvokeHandler中会处理
                throw e;
            }
        }
        log.info("AllocateOrderWriteServiceImpl.returnOrder.result :" + response==null?"":JSON.toJSONString(response));

        return Result.success(response);
    }


    @FacadeInvoker(value = "调拨单交货",errorCode = "OTS-03-005-00-15-046")
    @Override
    public Result<AllocateOrderDeliveryResponse> delivered(AllocateOrderDeliveryRequest allocateOrderDeliveryRequest) {
        log.info("AllocateOrderWriteServiceImpl.delivered.request:" + JSON.toJSONString(allocateOrderDeliveryRequest));
        AllocateOrderDeliveryResponse response = new AllocateOrderDeliveryResponse();
        if (StringUtils.isAllBlank(allocateOrderDeliveryRequest.getOutAllocateOrderId(), allocateOrderDeliveryRequest.getCode())) {
            log.info("deliveredFreshSaleOrderReturn, code:{}, outAllocateOrderId:{}", allocateOrderDeliveryRequest.getCode(), allocateOrderDeliveryRequest.getOutAllocateOrderId());
            return Result.success(response);
        }
        AllocateOrderRequest allocateOrderRequest = new AllocateOrderRequest();
        // allocateOrderRequest.setAllocateOrderCode(allocateOrderDeliveryRequest.getCode());
        allocateOrderRequest.setOutAllocateOrderId(allocateOrderDeliveryRequest.getOutAllocateOrderId());
        AllocateOrderWithOrderDetailsSDO sdo = allocateOrderReadRepository.get(allocateOrderRequest);
        if (sdo == null || sdo.getAllocateOrderSDO() == null || sdo.getAllocateOrderSDO().getId() == null){
            log.error("AllocateOrderWriteServiceImpl.delivered.error:{}",JSON.toJSONString(allocateOrderDeliveryRequest));
        }
        if (StringUtils.isEmpty(getOutAllocateOrderId(sdo))) {
            log.error("AllocateOrderWriteServiceImpl.delivered.outAllocateOrderId.isEmpty");
            throw new FacadeException("OTS-03-005-00-15-046");
        }
        //交货日志写在下一层
        try(RedisLock lock = new RedisLock(BcOrderConstants.REDIS_CACHE_CODE(),ALLOCATE_ORDER_LOCK_SYNC + getOutAllocateOrderId(sdo),5 * 60 * 1000,10 * 60 * 1000)){
            if (!lock.lock()){
                throw new FacadeException("OTS-03-005-00-15-096");
            }
            response = allocateOrderWriteRepository.delivered(allocateOrderDeliveryRequest);
        }catch (Exception e){
            log.error("allocateOrderDeliveredError",e);
            throw new FacadeException("OTS-03-005-00-15-046");
        }
//        AllocateOrderDeliveryResponse response = allocateOrderWriteRepository.delivered(allocateOrderDeliveryRequest);
        log.info("AllocateOrderWriteServiceImpl.delivered.result:" + JSON.toJSONString(response));
        return Result.success(response);
    }

    @Override
    @FacadeInvoker(value = "交货单删除撤销交货", errorCode = "OTS-03-005-00-15-051")
    public Result<AllocateOrderCancelDeliveredResponse> cancelDelivered(AllocateOrderCancelDeliveredRequest allocateOrderCancelDeliveredRequest) {
        //日志写在下一层
        log.info("调拨单交货单:" + JSON.toJSONString(allocateOrderCancelDeliveredRequest));
        AllocateOrderCancelDeliveredResponse response = new AllocateOrderCancelDeliveredResponse();
        //获取删除的交货单
        ConsignmentOrderLoadListRequest request = new ConsignmentOrderLoadListRequest();
        request.setOuterDeliveryOrderId(SapAndCrmFieldDealUtils.generateIdField(allocateOrderCancelDeliveredRequest.getDeliveryOrderId()));
        Result<List<ConsignmentOrder>> loadConsignmentOrderList = consignmentOrderService.loadConsignmentOrderList(request);
        if (loadConsignmentOrderList == null || CollectionUtils.isEmpty(loadConsignmentOrderList.getResult())) {
            return Result.success(response);
        }
        List<String> outCodes = loadConsignmentOrderList.getResult().stream().map(ConsignmentOrder::getOuterStoOrderId)
                .filter(StringUtils::isNotBlank).distinct().collect(Collectors.toList());
        if (CollectionUtils.isEmpty(outCodes)) {
            return Result.success(response);
        }
        String code = outCodes.get(0);
        AllocateOrderLoadListRequest allocateOrderLoadListRequest = new AllocateOrderLoadListRequest();
        code = InfoUtil.addZeroPrefix(code);
        allocateOrderLoadListRequest.setOutAllocateOrderId(code);
        Result<List<AllocateOrder>> listResult = allocateOrderService.loadAllocateOrderList(allocateOrderLoadListRequest);
        if (listResult == null || CollectionUtils.isEmpty(listResult.getResult())){
            return Result.success(response);
        }
        AllocateOrder allocateOrder = listResult.getResult().get(0);
        if (StringUtils.isEmpty(allocateOrder.getOutAllocateOrderId())) {
            log.error("AllocateOrderWriteServiceImpl.cancelDelivered.outAllocateOrderId.isEmpty");
            throw new FacadeException("OTS-03-005-00-15-051");
        }
        try(RedisLock lock = new RedisLock(BcOrderConstants.REDIS_CACHE_CODE(),ALLOCATE_ORDER_LOCK_SYNC + allocateOrder.getOutAllocateOrderId(),5 * 60 * 1000,10 * 60 * 1000)){
            if (!lock.lock()){
                throw new FacadeException("OTS-03-005-00-15-096");
            }
            response = allocateOrderWriteRepository.cancelDelivered(allocateOrderCancelDeliveredRequest);
        }catch (Throwable e){
            log.error("allocateOrderCancelDeliveredError",e);
            throw new FacadeException("OTS-03-005-00-15-099");
        }

//        response = allocateOrderWriteRepository.cancelDelivered(allocateOrderCancelDeliveredRequest);
        return Result.success(response);
    }

    @FacadeInvoker(value = "调拨单回调",errorCode = "OTS-03-005-00-15-039")
    @Override
    public Result<Void> notify(AllocateOrderListNotifyRequest allocateOrderListNotifyRequest) {
        log.info("AllocateOrderWriteServiceImpl#notify#request#AllocateOrderWriteServiceImpl#notify#sap012#callback:" + JSON.toJSONString(allocateOrderListNotifyRequest));
        List<OrderSDO> orderSDOS = new ArrayList<>();
        Map<String, String> failMsgMap = new HashMap<>();
        for (AllocateOrderNotifyRequest allocateOrderNotifyRequest : allocateOrderListNotifyRequest.getData()) {
            //012接口回调需要区分是调拨还是销售（F-B销售订单及老的调拨单都是调sap 012接口）
            QuerySingleRequest queryRequest = new QuerySingleRequest();
            queryRequest.setMainOrderId(convertTradeOrderId(allocateOrderNotifyRequest.getInnerKey()));
            log.info("012 callback, query sale order param:{}", JSON.toJSONString(queryRequest));
            OrderSDO orderSDO = orderQueryService.querySingle(queryRequest);
            log.info("012 callback, query sale order result:{}", JSON.toJSONString(orderSDO));
            //调拨单过来时,查不到,该接口返回了 {"creatorId":"","features":{},"id":"null","orderLineSDOS":[]}
            if (Objects.nonNull(orderSDO) && !"null".equals(orderSDO.getId())) {
                try {
                    String outerKey = null;
                    if (Objects.nonNull(allocateOrderNotifyRequest.getOuterKey())) {
                        outerKey = allocateOrderNotifyRequest.getOuterKey().replaceFirst("^0*", "");
                    }
                    //如果是销售订单，则更新订单信息
                    ModifyOrderRequest modifyOrderRequest = new ModifyOrderRequest();
                    modifyOrderRequest.setSuccess(allocateOrderNotifyRequest.getSuccess());
                    modifyOrderRequest.setOuterKey(outerKey);
                    modifyOrderRequest.setOutOrderId(outerKey);
                    modifyOrderRequest.setInnerKey(allocateOrderNotifyRequest.getInnerKey());
                    modifyOrderRequest.setTradeOrderId(allocateOrderNotifyRequest.getInnerKey());
                    modifyOrderRequest.setMessage(allocateOrderNotifyRequest.getMessage());
                    log.info("012#callback#sale#order#update#param={}", JSON.toJSONString(modifyOrderRequest));
                    String updateSaleOrderResult = updateSaleOrder(modifyOrderRequest);
                    log.info("012#callback#sale#order#update#result={}", JSON.toJSONString(updateSaleOrderResult));
                    orderSDOS.add(orderSDO);

                    if (allocateOrderNotifyRequest.getSuccess()) {
                        // 发送MQ消息通知其他中心
                        CommonAsyncMessageResponse commonAsyncMessageResponse = new CommonAsyncMessageResponse();
                        commonAsyncMessageResponse.setEventCode(InnerMessageEventEnum.SALES_ORDER_CREATED_SAP.getValue());
                        commonAsyncMessageResponse.setEventName(InnerMessageEventEnum.SALES_ORDER_CREATED_SAP.getLabel());
                        //如果是退货订单，则将发货仓库和收货仓库调换
                        if (SetOrderTag.RETURN.equals(orderSDO.getOrderTag())){
                            String wareHouseCode = orderSDO.getWareHouseCode();
                            String wareHouseName = orderSDO.getWareHouseName();
                            orderSDO.setWareHouseCode(orderSDO.getReceiveWareHouseCode());
                            orderSDO.setWareHouseName(orderSDO.getReceiveWareHouseName());
                            orderSDO.setReceiveWareHouseCode(wareHouseCode);
                            orderSDO.setReceiveWareHouseName(wareHouseName);
                            String wareHouseOutCode = orderSDO.getWareHouseOutCode();
                            orderSDO.setWareHouseOutCode(orderSDO.getReceiveWareHouseOutCode());
                            orderSDO.setReceiveWareHouseOutCode(wareHouseOutCode);
                        }
                        commonAsyncMessageResponse.setData(Lists.newArrayList(orderSDO));
                        log.info("AllocateOrderWriteServiceImpl#notify#commonAsyncMessageResponse: {}", commonAsyncMessageResponse);
                        if (OrderCategoryEnum.STOCK.getCode().equals(orderSDO.getOrderCategoryCode())) {

                            HandleGroupCallbackResponse response = handleGroupCallback(orderSDO, allocateOrderNotifyRequest,Boolean.TRUE);
                            log.info("AllocateOrderWriteServiceImpl#notify#success={}",response.getIsSuccess());
                            if (response.getIsSuccess()) {
                                setCommonAsyncMessageResponse(response,commonAsyncMessageResponse);
                                Boolean sendSuccess = epochMessageService.sendMessage("allocate_order_message", "CR_SALEORDER_V2", "SalesOrderEvent",
                                        JSONObject.toJSONString(commonAsyncMessageResponse));
                                log.info("AllocateOrderWriteServiceImpl#notify#sendSuccess={}",sendSuccess);
                            }
                        } else {
                            epochMessageService.sendMessage("allocate_order_message", "CR_SALEORDER_V2", "SalesOrderEvent",
                                    JSONObject.toJSONString(commonAsyncMessageResponse));
                        }
                    } else {
                        //F-B失败订单需要记录到B-b订单的feature
                        if (OrderCategoryEnum.STOCK.getCode().equals(orderSDO.getOrderCategoryCode())) {
                            log.info("AllocateOrderWriteServiceImpl#notify#fail");
                            //判断关联的B-b订单是否成功返回了
                            handleGroupCallback(orderSDO, allocateOrderNotifyRequest,Boolean.FALSE);
                        }
                        failMsgMap.put(orderSDO.getOrderBizId(),allocateOrderNotifyRequest.getMessage());
                    }
                } catch (Exception e) {
                    log.error("orderWriteService modifySaleOrder failed", e);
                    log.error("orderWriteService modifySaleOrder failed:{}", e.fillInStackTrace());
                    throw e;
                }
            } else {
                log.info("012 callback, old route");
                AllocateOrderNotifyResponse response = allocateOrderWriteRepository.notify(allocateOrderNotifyRequest);
            }
        }
        log.info("AllocateOrderWriteServiceImpl notify sap012 end");

        // 发送kafka消息
        if (CollectionUtils.isNotEmpty(orderSDOS) && !OrderCategoryEnum.STOCK.getCode().equals(orderSDOS.get(0).getOrderCategoryCode())){
            sendOrderInfoToNYK(orderSDOS,failMsgMap);
        }

        return Result.success(null);
    }
    private void setCommonAsyncMessageResponse(HandleGroupCallbackResponse handleGroupCallbackResponse, CommonAsyncMessageResponse commonAsyncMessageResponse){
        QuerySingleRequest bizQueryRequest = new QuerySingleRequest();
        bizQueryRequest.setMainOrderId(handleGroupCallbackResponse.getId());
        log.info("012#callback#query#sale#order#param:{}", JSON.toJSONString(bizQueryRequest));
        OrderSDO bizOrderSDO = orderQueryService.querySingle(bizQueryRequest);
        log.info("012#callback#query#sale#order#bizOrderSDO:{}", JSON.toJSONString(bizOrderSDO));
        commonAsyncMessageResponse.setData(Lists.newArrayList(bizOrderSDO));
    }
    private void sendOrderInfoToNYK(List<OrderSDO> orderSDOS,Map<String,String> failMsgMap) {
        log.info("sendOrderInfoToNYK_request_v12,orderSDOS:{},failMsgMap:{}",JSON.toJSONString(orderSDOS),JSON.toJSONString(failMsgMap));

        // 发送kafka消息要传的内容
        String fromSys = "";
        String businessUnitCode = "";

        List<PushOrderInfoToMnSideRequestOrderInfo> pushOrderInfoList = new ArrayList<>();
        for (OrderSDO order : orderSDOS) {
            if (!checkOrderCenterTag(order.getFeatures())) {
                // 非订单中心订单不发kafka消息
                return;
            }

            if (org.apache.commons.lang.StringUtils.isBlank(fromSys)) fromSys = order.getFromSys();
            if (org.apache.commons.lang.StringUtils.isBlank(businessUnitCode)) businessUnitCode = order.getBusinessUnitOutCode();

            pushOrderInfoList.add(PushOrderInfoToMnSideRequestOrderInfo.builder()
                    .salesOrderNo(order.getOrderBizId())
                    .orderStatus(failMsgMap.containsKey(order.getOrderBizId()) ? SalesOrderPushStatusEnum.FAIL_CREATE.getOrderStatusCode() : SalesOrderPushStatusEnum.SUCCESS_CREATE.getOrderStatusCode())
                    .message(failMsgMap.get(order.getOrderBizId()))
                    .build());
        }
        if (CollectionUtils.isEmpty(pushOrderInfoList)) {
            return;
        }

        CommonAsyncMessageResponse commonAsyncMessageResponse = new CommonAsyncMessageResponse();
        commonAsyncMessageResponse.setEventCode(AsyncMessageEventEnum.SALES_ORDER_CREATE.getValue());
        commonAsyncMessageResponse.setEventName(AsyncMessageEventEnum.SALES_ORDER_CREATE.getLabel());
        commonAsyncMessageResponse.setEventTime(String.valueOf(new Date().getTime()));
        commonAsyncMessageResponse.setFromSys(fromSys);
        commonAsyncMessageResponse.setBusinessUnitCode(businessUnitCode);
        commonAsyncMessageResponse.setData(pushOrderInfoList);

        log.info("SendMsgToSendKafka_MQ_Result_V2_param,Request={}", JSON.toJSONString(commonAsyncMessageResponse));

        Boolean sendResult = messageService.sendMessage("allocate_order_message", "CR_SALEORDER_V2", "SaleOrderPushMn",
                JSON.toJSONString(commonAsyncMessageResponse));

        log.info("SendMsgToSendKafka_MQ_Result_V2,Request={}, Resp={}",
                JSON.toJSONString(commonAsyncMessageResponse), JSON.toJSONString(sendResult));
    }

    /**
     * 判断是否为订单中心订单
     */
    public boolean checkOrderCenterTag(Map<String, String> features) {
        String os = features.get("os");
        if (org.apache.commons.lang.StringUtils.isBlank(os) || !TAG_ORDER_CENTER.equals(os)) {
            return false;
        }
        return true;
    }

    private HandleGroupCallbackResponse handleGroupCallback(OrderSDO orderSDO, AllocateOrderNotifyRequest allocateOrderNotifyRequest,Boolean isSuccess) {
        HandleGroupCallbackResponse response = new HandleGroupCallbackResponse();
        //判断关联的B-b订单是否成功返回了
        QueryByOrderBizIdsRequest queryByOrderBizIdRequest= new QueryByOrderBizIdsRequest();
        List<String> list = new ArrayList<>();
        list.add(orderSDO.getRelationNo());
        queryByOrderBizIdRequest.setOrderBizIds(list);
        if (CollectionUtils.isEmpty(list)) {
            log.info("checkCreditB list={}",JSON.toJSONString(list));
            response.setIsSuccess(Boolean.FALSE);
            return response;
        }
        //B-b订单
        Result<List<Order>> bizOrderList = orderService.queryByOrderBizIds(queryByOrderBizIdRequest);
        log.info("handleGroupCallback#queryByOrderBizIdRequest={}", JSON.toJSONString(queryByOrderBizIdRequest));
        log.info("handleGroupCallback#bizOrderList={}",JSON.toJSONString(bizOrderList));
        if(ObjectUtil.isNull(bizOrderList) || CollUtil.isEmpty(bizOrderList.getResult())){
            response.setIsSuccess(Boolean.FALSE);
            return response;
        }

        Map<String, String> features = new HashMap<>();
        Order bizOrder = bizOrderList.getResult().get(0);
        if (StringUtils.isNotBlank(bizOrder.getFeatures())) {
            features = JSON.parseObject(bizOrder.getFeatures(), Map.class);
        }
        features.put(SaleOrderConstants.GROUP_CALLBACK_IS_SUCCESS, String.valueOf(isSuccess));
        features.put(SaleOrderConstants.GROUP_EXT_MESSAGE, allocateOrderNotifyRequest.getMessage());
//        bizOrder.setFeatures(JSON.toJSONString(features));
//        log.info("handleGroupCallback#bizOrder={}",JSON.toJSONString(bizOrder));
        //修改B-b订单的feature
//        UpdateOrderResponse updateOrderResponse = orderService.updateOrder(bizOrder);
        UpdateFeaturesRequest request =  new UpdateFeaturesRequest();
        request.setFeatures(JSON.toJSONString(features));
        request.setSyncVersion(bizOrder.getSyncVersion());
        request.setId(String.valueOf(bizOrder.getId()));
        try{
            //更新失败需要重试
            log.info("handleGroupCallback#request={}",JSON.toJSONString(request));
            UpdateFeaturesResponse updateFeaturesResponse = orderService.updateFeatures(request);
            log.info("handleGroupCallback#updateFeaturesResponse={}",JSON.toJSONString(updateFeaturesResponse));
            if(!Long.valueOf("1").equals(updateFeaturesResponse.getCount())){
                Result<List<Order>> second = orderService.queryByOrderBizIds(queryByOrderBizIdRequest);
                if(ObjectUtil.isNull(second) || CollUtil.isEmpty(second.getResult())){
                    response.setIsSuccess(Boolean.FALSE);
                    return response;
                }
                Order secondOrder = second.getResult().get(0);
                UpdateFeaturesRequest secondRequest =  new UpdateFeaturesRequest();
                secondRequest.setFeatures(JSON.toJSONString(features));
                secondRequest.setSyncVersion(secondOrder.getSyncVersion());
                secondRequest.setId(String.valueOf(secondOrder.getId()));
                log.info("handleGroupCallback#secondRequest={}",JSON.toJSONString(request));
                try{
                    UpdateFeaturesResponse secondUpdateFeaturesResponse = orderService.updateFeatures(secondRequest);
                    log.info("handleGroupCallback#secondUpdateFeaturesResponse={}",JSON.toJSONString(secondUpdateFeaturesResponse));
                }catch (Exception secondE){
                    log.error("第二次更新features失败",secondE);
                }
            }
        }catch (Exception e){
            log.error("第一次更新失败",e);
        }

        //判断是否B-b订单已经成功
        response.setIsSuccess(isSuccess&&features.containsKey(SaleOrderConstants.EXT_CALLBACK_IS_SUCCESS) && SaleOrderConstants.SUCCESS.equals(features.get(SaleOrderConstants.EXT_CALLBACK_IS_SUCCESS)));
        response.setId(String.valueOf(bizOrder.getId()));
        return response;
    }

    private Result<Boolean>  updateSaleOrderV2(ModifyOrderRequest request, OrderQueryResponse orderQueryResponse) {
        log.info("updateSaleOrderV2 param,request:{}, orderQueryResponse:{}", JSON.toJSONString(request), JSON.toJSONString(orderQueryResponse));
        OrderSDO orderSDO = new OrderSDO();
        orderSDO.setId(orderQueryResponse.getId());
        if (org.apache.commons.lang.StringUtils.isNotBlank(orderQueryResponse.getOrderStatus())) {
            // 非已推送或待推送状态的销售单不允许推进为待交货
            if (!checkOrderStatus(Integer.parseInt(orderQueryResponse.getOrderStatus()))) {
                log.info("sapCallBackV2|当前订单:" + request.getInnerKey() + "|状态:" + orderQueryResponse.getOrderStatus() + "|不允许推进");
                return Result.fail(null, "当前订单状态不允许推进");
            }
        }
        if (StringUtils.isEmpty(request.getOuterKey())) {
            return Result.fail(null, "外部单号不能为空");
        }
        if (request.getSuccess()) {
            orderSDO.setOuterOrderId(request.getOuterKey());
            if (orderSDO.getOrderTag().equals("return")) {
                orderSDO.setStatus(OrderStatus.WAIT_RECEIVING.getCode());
                if (CollectionUtils.isNotEmpty(orderSDO.getOrderLineSDOS())) {
                    orderSDO.getOrderLineSDOS().forEach(SetOrderTag::setExchangeOrderLineSDOStatus);
                }
            } else if (orderSDO.getOrderTag().equals("exchange")) {
                orderSDO.setStatus(OrderStatus.WAIT_HANDLE.getCode());
                if (CollectionUtils.isNotEmpty(orderSDO.getOrderLineSDOS())) {
                    orderSDO.getOrderLineSDOS().forEach(SetOrderTag::setExchangeOrderLineSDOStatus);
                }
            } else {
                orderSDO.setStatus(OrderStatus.BEFORE_DELIVERY.getCode());
            }
        } else {
            orderSDO.setStatus(OrderStatus.STAY_PUSH.getCode());
        }
        Result<Boolean> result = orderWriteService.modifySaleOrderSimple(orderSDO);
        log.info("updateSaleOrderV2 param,result:{}", JSON.toJSONString(result));
        return result;
    }

    private String updateSaleOrder(ModifyOrderRequest request) {
        StringBuilder stringBuilder = new StringBuilder();
        //request去除单引号字符 因为纪元无法识别单引号，导致报错
        if (org.apache.commons.lang.StringUtils.isNotBlank(request.getMessage())) {
            request.setMessage(request.getMessage().replace("'", ""));
        }
        // sap报文里的订单id转换成内部主键id  sap: 1开头 中间补0  总长度10
        request.setInnerKey(convertTradeOrderId(request.getInnerKey()));
        String lockBackKey = "sapBackSync:" + request.getInnerKey();
        if (request.getSuccess()) {
            // sap同步成功、外部单号不能为空
            if (org.apache.commons.lang.StringUtils.isBlank(request.getOuterKey())) {
                stringBuilder.append(request.getInnerKey()).append(",外部单号不能为空\n");
                return stringBuilder.toString();
            }

            OrderQueryRequest queryRequest = new OrderQueryRequest();
            queryRequest.setId(request.getInnerKey());
            Result<OrderQueryResponse> query = orderQueryService.query(queryRequest);
            if (null == query || !query.isSuccess() || null == query.getResult()) {
                stringBuilder.append(request.getInnerKey()).append(",订单不存在\n");
                return stringBuilder.toString();
            }
            request.setTradeOrderId(query.getResult().getOrderBizId());

            if (org.apache.commons.lang.StringUtils.isNotBlank(query.getResult().getOrderStatus())) {
                // 非已推送或待推送状态的销售单不允许推进为待交货
                if (!checkOrderStatus(Integer.parseInt(query.getResult().getOrderStatus()))) {
                    log.info("sapCallBackV2|当前订单:" + request.getInnerKey() + "|状态:" + query.getResult().getOrderStatus() + "|不允许推进");
                    return stringBuilder.toString();
                }
            }
            OrderSDO orderSDO = SaveOrderConverter.respConverterSDO(query.getResult());
            orderSDO.setOuterOrderId(request.getOuterKey());
            if (orderSDO.getOrderTag().equals("return")) {
                orderSDO.setStatus(OrderStatus.WAIT_RECEIVING.getCode());
                if (CollectionUtils.isNotEmpty(orderSDO.getOrderLineSDOS())) {
                    orderSDO.getOrderLineSDOS().forEach(SetOrderTag::setExchangeOrderLineSDOStatus);
                }
            } else if (orderSDO.getOrderTag().equals("exchange")) {
                orderSDO.setStatus(OrderStatus.WAIT_HANDLE.getCode());
                if (CollectionUtils.isNotEmpty(orderSDO.getOrderLineSDOS())) {
                    orderSDO.getOrderLineSDOS().forEach(SetOrderTag::setExchangeOrderLineSDOStatus);
                }
            } else {
                orderSDO.setStatus(OrderStatus.BEFORE_DELIVERY.getCode());
                if (CollectionUtils.isNotEmpty(orderSDO.getOrderLineSDOS())) {
                    for (OrderLineSDO orderLineSDO : orderSDO.getOrderLineSDOS()) {
                        log.info("OrderService.sapCallBackV2 orderLineSDO={}", JSON.toJSONString(orderLineSDO));
                        // 已取消状态不允许推进到待交货
                        if (!OrderStatus.CANCELLED.getCode().equals(orderLineSDO.getStatus())) {
                            orderLineSDO.setStatus(OrderStatus.BEFORE_DELIVERY.getCode());
                        }
                    }
                }
            }

            if (MapUtils.isNotEmpty(orderSDO.getFeatures())) {
                orderSDO.getFeatures().put(SaleOrderConstants.EXT_MESSAGE, request.getMessage());
            } else {
                Map<String, String> map = new HashMap<>();
                map.put(SaleOrderConstants.EXT_MESSAGE, request.getMessage());
                orderSDO.setFeatures(map);
            }
            Map<String,String> bizFeaturesMap  = new HashMap<>();
            String bizFeatures = orderSDO.getBizFeatures();
            log.info("updateSaleOrder success sync before bizFeatures = {}",bizFeatures);
            if(StringUtils.isNotBlank(bizFeatures)){
                bizFeaturesMap = JSON.parseObject(bizFeatures, Map.class);
            }
            bizFeaturesMap.put(SaleOrderConstants.EXT_MESSAGE,request.getMessage());
            orderSDO.setBizFeatures(JSON.toJSONString(bizFeaturesMap));
            log.info("updateSaleOrder success sync after bizFeatures = {}",JSON.toJSONString(bizFeaturesMap));
            Result<Boolean> updateResult = updateSapSyncSaleOrderStatus(orderSDO);
        } else {
            OrderQueryRequest queryRequest = new OrderQueryRequest();
            queryRequest.setId(request.getInnerKey());
            Result<OrderQueryResponse> query = orderQueryService.query(queryRequest);
            if (null == query || !query.isSuccess() || null == query.getResult()) {
                stringBuilder.append(request.getInnerKey()).append(",订单不存在\n");
                return stringBuilder.toString();
            }
            request.setTradeOrderId(query.getResult().getOrderBizId());
            if (org.apache.commons.lang.StringUtils.isNotBlank(query.getResult().getOrderStatus())) {
                if (!OrderStatus.PUSHING.getCode().equals(Integer.parseInt(query.getResult().getOrderStatus()))) {
                    return stringBuilder.toString();
                }
            }
            OrderSDO orderSDO = SaveOrderConverter.respConverterSDO(query.getResult());
            // sap回调时,若同步sap状态失败,将销售单状态置为待推送,并记录错误信息
            //外部单号不为空 考虑重复提交，sap返回了false，订单重复创建说明
            if (org.apache.commons.lang.StringUtils.isBlank(orderSDO.getOuterOrderId())) {
                orderSDO.setStatus(OrderStatus.STAY_PUSH.getCode());
                //子单状态也变更
                for (OrderLineSDO x : orderSDO.getOrderLineSDOS()) {
                    x.setStatus(OrderStatus.STAY_PUSH.getCode());
                }
            } else {
                return stringBuilder.toString();
            }
            if (MapUtils.isNotEmpty(orderSDO.getFeatures())) {
                String pushCount = orderSDO.getFeatures().get(SaleOrderConstants.PUSH_COUNT);
                Integer count = pushCount == null ? 1 : Integer.parseInt(pushCount) + 1;
                orderSDO.getFeatures().put(SaleOrderConstants.EXT_MESSAGE, request.getMessage());
                orderSDO.getFeatures().put(SaleOrderConstants.PUSH_COUNT, String.valueOf(count));
            } else {
                Map<String, String> map = new HashMap<>();
                map.put(SaleOrderConstants.EXT_MESSAGE, request.getMessage());
                map.put(SaleOrderConstants.PUSH_COUNT, "1");
                orderSDO.setFeatures(map);
            }
            Map<String,String> bizFeaturesMap  = new HashMap<>();
            String bizFeatures = orderSDO.getBizFeatures();
            log.info("updateSaleOrder fail sync before bizFeatures = {}",bizFeatures);
            if(StringUtils.isNotBlank(bizFeatures)){
                bizFeaturesMap = JSON.parseObject(bizFeatures, Map.class);
            }
            bizFeaturesMap.put(SaleOrderConstants.EXT_MESSAGE,request.getMessage());
            log.info("updateSaleOrder fail sync after bizFeatures = {}",JSON.toJSONString(bizFeaturesMap));
            orderSDO.setBizFeatures(JSON.toJSONString(bizFeaturesMap));
            Result<Boolean> updateResult = updateSapSyncSaleOrderStatus(orderSDO);
        }
        return null;
    }

    private Result<Boolean> updateSapSyncSaleOrderStatus(OrderSDO orderSDO) {
        OrderOutCodeAssmeble.assembleOrderLineOutCode(orderSDO);
        log.info("AllocateOrderWriteServiceImpl#updateSapSyncSaleOrderStatus orderSDO={}", JSON.toJSONString(orderSDO));
        Result<Boolean> result = orderWriteService.modifySaleOrderSimple(orderSDO);
//        OrderWriteEntity orderWriteEntity = OrderWriteFactory.updateOrder(orderSDO);
//        Result<Boolean> result = orderWriteEntity.modify();

        //记录操作日志
        OpLogModel.OpLogModelBuilder<String> builder = OpLogModel.builder();
        OpLogModel<String> opLogModel = builder.build();
        opLogModel.setModuleName(ModuleEnum.SALES.getModuleName());
        opLogModel.setModuleId(String.valueOf(ModuleEnum.SALES.getModuleId()));
        opLogModel.setObjectId(orderSDO.getId());
        opLogModel.setObjectName("id");
        opLogModel.setOperateType(OrderOperateType.EDIT.getCode());
        opLogModel.setOperatorType(OperatorType.USER.getOperatorType());
        Map<String, String> map = new HashMap<>();
        map.put("name", "系统");
        map.put("active", OrderOperateType.EDIT.getDesc());
        map.put("bizNo", orderSDO.getOrderBizId());
        if (org.apache.commons.lang3.StringUtils.isNotBlank(orderSDO.getOrderType())){
            SaleOrderTag saleOrderTag = SaleOrderTag.of(orderSDO.getOrderType());
            if (Objects.nonNull(saleOrderTag)){
                map.put("orderType",saleOrderTag.getDesc());
            }
        }
        if (Objects.nonNull(orderSDO.getStatus())){
            OrderStatus orderStatus = OrderStatus.of(orderSDO.getStatus());
            if (Objects.nonNull(orderStatus)){
                map.put("status",orderStatus.getDesc());
            }
        }
        String logInfo = JSON.toJSONString(map);
        opLogModel.setFeatures(logInfo);
        log.asyncMonitorObjectChangeV2(opLogModel);
        return result;
    }

    private boolean checkOrderStatus(int status) {
        if (OrderStatus.PUSHING.getCode().equals(status) || OrderStatus.STAY_PUSH.getCode().equals(status)
                || OrderStatus.BEFORE_DELIVERY.getCode().equals(status) || OrderStatus.WAIT_HANDLE.getCode().equals(status)
                || OrderStatus.WAIT_RECEIVING.getCode().equals(status)) {
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }

    /**
     * sap报文里的订单id转换成内部主键id  sap: 1开头 中间补0  总长度10
     * @param sapOrderId
     * @return
     */
    private String convertTradeOrderId(String sapOrderId){
        if (StringUtils.isNotBlank(sapOrderId)){
            return sapOrderId.trim().length() == 10 ?sapOrderId.trim().replaceFirst("^10+","")
                    : sapOrderId.trim();
        }
        return null;
    }

    /**
     * 撤回发货
     * @param allocateOrderCancelShippedRequest
     * @return
     */
    @Override
    @FacadeInvoker(value = "调拨单撤销发货",errorCode = "OTS-03-005-00-15-038")
    public Result<AllocateOrderCancelShippedResponse> cancelShipped(AllocateOrderCancelShippedRequest allocateOrderCancelShippedRequest) {
        log.info("materialHandle_cancelShipped_request,material:{}, request:{}", allocateOrderCancelShippedRequest.getOutMaterialCertificateId(), JSON.toJSONString(allocateOrderCancelShippedRequest));
        AllocateOrderRequest allocateOrderRequest = new AllocateOrderRequest();
        allocateOrderRequest.setAllocateOrderId(allocateOrderCancelShippedRequest.getId());
        AllocateOrderWithOrderDetailsSDO sdo = allocateOrderReadRepository.get(allocateOrderRequest);
        if (StringUtils.isEmpty(getOutAllocateOrderId(sdo))) {
            log.error("AllocateOrderWriteServiceImpl.cancelShipped.outAllocateOrderId.isEmpty");
            throw new FacadeException("OTS-03-005-00-15-038");
        }
        AllocateOrderCancelShippedResponse response = new AllocateOrderCancelShippedResponse();
        try(RedisLock lock = new RedisLock(BcOrderConstants.REDIS_CACHE_CODE(),ALLOCATE_ORDER_LOCK_SYNC + getOutAllocateOrderId(sdo),5 * 60 * 1000,10 * 60 * 1000)){
            if (!lock.lock()){
                throw new FacadeException("OTS-03-005-00-15-096");
            }
            response = allocateOrderWriteRepository.cancelShipped(allocateOrderCancelShippedRequest);
        }catch (Exception e){
            log.error("allocateOrderCancelShippedError",e);
            throw new FacadeException("OTS-03-005-00-15-038");
        }
        AllocateOrderWithOrderDetailsSDO fresh = allocateOrderReadRepository.get(allocateOrderRequest);
        saveDocumentLog(sdo,fresh, AllocateApplyOrderOperationTypeEnum.CANCELSHIPPED,
                OperatorType.INNER_INTERFACE.getOperatorType(),allocateOrderCancelShippedRequest.getSendTime());
        log.info("materialHandle_cancelShipped_response,material:{}, request:{}", allocateOrderCancelShippedRequest.getOutMaterialCertificateId(), JSON.toJSONString(response));
        return Result.success(response);
    }
    @Override
    @FacadeInvoker(value = "调拨单取消收货" ,errorCode = "OTS-03-005-00-15-055")
    /**
     * 撤销收货
     */
    public Result<AllocateOrderCancelReceivedResponse> cancelReceived(AllocateOrderCancelReceivedRequest allocateOrderCancelReceivedRequest) {
        log.info("materialHandle_cancelReceived_request,material:{}, request:{}", allocateOrderCancelReceivedRequest.getOutMaterialCertificateId(), JSON.toJSONString(allocateOrderCancelReceivedRequest));
        AllocateOrderRequest request = new AllocateOrderRequest();
        request.setAllocateOrderId(allocateOrderCancelReceivedRequest.getId());
        AllocateOrderWithOrderDetailsSDO old = allocateOrderReadRepository.get(request);
        if (StringUtils.isEmpty(getOutAllocateOrderId(old))) {
            log.error("AllocateOrderWriteServiceImpl.cancelReceived.outAllocateOrderId.isEmpty");
            throw new FacadeException("OTS-03-005-00-15-055");
        }
        AllocateOrderCancelReceivedResponse response = new AllocateOrderCancelReceivedResponse();
        try(RedisLock lock = new RedisLock(BcOrderConstants.REDIS_CACHE_CODE(),ALLOCATE_ORDER_LOCK_SYNC + getOutAllocateOrderId(old),5 * 60 * 1000,10 * 60 * 1000)){
            if (!lock.lock()){
                throw new FacadeException("OTS-03-005-00-15-096");
            }
            response = allocateOrderWriteRepository.cancelReceived(allocateOrderCancelReceivedRequest);
        }catch (Exception e){
            log.error("allocateOrderCancelReceivedError",e);
            throw new FacadeException("OTS-03-005-00-15-055");
        }
        AllocateOrderWithOrderDetailsSDO fresh = allocateOrderReadRepository.get(request);
        saveDocumentLog(old,fresh,AllocateApplyOrderOperationTypeEnum.CANCELRECEIVED,
                OperatorType.INNER_INTERFACE.getOperatorType(),allocateOrderCancelReceivedRequest.getSendTime());
        log.info("materialHandle_cancelReceived_response,material:{}, request:{}", allocateOrderCancelReceivedRequest.getOutMaterialCertificateId(), JSON.toJSONString(response));
        return Result.success(response);
    }

//    private void saveDocumentLog(AllocateOrderInfo old,
//                                 AllocateOrderInfo fresh,
//                                 String operateType,
//                                 Integer operatorType,
//                                 Map<String,String> features){
//        try{
//            LoginUser loginUser = epochLoginUserService.getLoginUser();
//            //因为日志表的长度限制, 现在日志没有用到子单明细,就把子单给缩略掉
//            if (old != null){
//                old.setDetails(Lists.newArrayList());
//            }
//            if (fresh != null){
//                fresh.setDetails(Lists.newArrayList());
//            }
//            OpLogModel<AllocateOrderInfo> opLogModel = OpLogModel.<AllocateOrderInfo>builder()
//                    .oldValue(old)
//                    .newValue(fresh)
//                    .moduleName(ModuleEnum.UNIFIED_INVENTORY.getModuleName())
//                    .moduleId(ModuleEnum.UNIFIED_INVENTORY.getModuleId())
//                    .dataType(OpLogDateTypeEnum.OBJECT_TYPE.getDataType())
//                    .operateType(StringUtils.isBlank(operateType)?null:Integer.parseInt(operateType))
//                    .operatorType(Optional.ofNullable(operatorType).orElse(OperatorType.USER.getOperatorType()))
//                    .operatorId(loginUser.getEmpId())
//                    .objectId(fresh.getId().toString())
//                    .objectName(fresh.getCode())
//                    .features(JSON.toJSONString(features))
////                    .objectId(fresh.getAllocateOrderSDO().getId().toString())
////                    .objectName(fresh.getAllocateOrderSDO().getCode())
//                    .build();
//            log.asyncMonitorObjectChangeV2(opLogModel);
//        }catch (Throwable e) {
//            log.error("调拨单日志写入失败:" , e);
//            //这里要吃掉异常，防止影响主流程
//            // throw e;
//        }
//    }

    /**
     * 调拨单日志写入
     * @param old
     * @param fresh
     */
    private void saveDocumentLog(AllocateOrderWithOrderDetailsSDO old,
                                 AllocateOrderWithOrderDetailsSDO fresh,
                                 AllocateApplyOrderOperationTypeEnum operateType,
                                 Integer operatorType,String sendTime){
        try{
            LoginUser loginUser = epochLoginUserService.getLoginUser();
            //因为日志表的长度限制, 现在日志没有用到子单明细,就把子单给缩略掉
            if (old != null){
                old.setAllocateOrderDetailSDOS(Lists.newArrayList());
            }
            if (fresh == null) {
                log.info("saveDocumentLog fresh is null");
                return;
            }
            fresh.setAllocateOrderDetailSDOS(Lists.newArrayList());
            if (Objects.isNull(fresh.getAllocateOrderSDO())) {
                fresh.setAllocateOrderSDO(new AllocateOrderSDO());
            }
            OpLogModel<AllocateOrderWithOrderDetailsSDO> opLogModel = OpLogModel.<AllocateOrderWithOrderDetailsSDO>builder()
                    .oldValue(old)
                    .newValue(fresh)
                    .moduleName(ModuleEnum.UNIFIED_INVENTORY.getModuleName())
                    .moduleId(ModuleEnum.UNIFIED_INVENTORY.getModuleId())
                    .dataType(OpLogDateTypeEnum.OBJECT_TYPE.getDataType())
                    .operateType(Integer.parseInt(operateType.getValue()))
                    .operatorType(Optional.ofNullable(operatorType).orElse(OperatorType.USER.getOperatorType()))
                    .operatorId(loginUser.getEmpId())
                    .objectId(String.valueOf(fresh.getAllocateOrderSDO().getId()))
                    .objectName(fresh.getAllocateOrderSDO().getCode())
                    .build();

            Map<String, String> map = new HashMap<>();
            map.put("name", loginUser.getRealName());

            map.put("active", EnumUtils.getAllocateApplyOrderOperationTypeEnum(Integer.parseInt(operateType.getValue())));

            map.put("bizNo", fresh.getAllocateOrderSDO().getCode());

            map.put("orderType",EnumUtils.getAllocateOrderType(fresh.getAllocateOrderSDO().getType()));

            if (Objects.nonNull(fresh.getAllocateOrderSDO().getStatus())){
                AllocateOrderStatus allocateOrderStatus = AllocateOrderStatus.of(fresh.getAllocateOrderSDO().getStatus());
                if (Objects.nonNull(allocateOrderStatus)){
                    map.put("status",allocateOrderStatus.getDesc());
                }
            }
            if (StringUtils.isNotBlank(sendTime)){
                map.put("sendTime",sendTime);
            }
            String logInfo = JSONObject.toJSONString(map);
            opLogModel.setFeatures(logInfo);

            log.asyncMonitorObjectChangeV2(opLogModel);
        }catch (Throwable e){
            log.error("调拨单日志写入失败:" , e);
            //这里要吃掉异常，防止影响主流程
            // throw  e;
        }
    }

    private void saveDocumentLogAllocate(AllocateOrderWithOrderDetailsSDO old,
                                         AllocateOrderWithOrderDetailsSDO fresh,
                                         OperateType operateType,
                                         Integer operatorType){
        try{
            LoginUser loginUser = epochLoginUserService.getLoginUser();
            //因为日志表的长度限制, 现在日志没有用到子单明细,就把子单给缩略掉
            if (old != null){
                old.setAllocateOrderDetailSDOS(Lists.newArrayList());
            }
            if (fresh != null){
                fresh.setAllocateOrderDetailSDOS(Lists.newArrayList());
            }
            OpLogModel<AllocateOrderWithOrderDetailsSDO> opLogModel = OpLogModel.<AllocateOrderWithOrderDetailsSDO>builder()
                    .oldValue(old)
                    .newValue(fresh)
                    .moduleName(ModuleEnum.UNIFIED_INVENTORY.getModuleName())
                    .moduleId(ModuleEnum.UNIFIED_INVENTORY.getModuleId())
                    .dataType(OpLogDateTypeEnum.OBJECT_TYPE.getDataType())
                    .operateType(operateType.getOperateType())
                    .operatorType(Optional.ofNullable(operatorType).orElse(OperatorType.USER.getOperatorType()))
                    .operatorId(Optional.ofNullable(fresh).map(AllocateOrderWithOrderDetailsSDO::getAllocateOrderSDO).map(AllocateOrderSDO::getUpdater).map(String::valueOf).orElse(loginUser.getEmpId()))
                    .objectId(Optional.ofNullable(fresh).map(AllocateOrderWithOrderDetailsSDO::getAllocateOrderSDO).map(AllocateOrderSDO::getId).map(String::valueOf).orElse(null))
                    .objectName(fresh.getAllocateOrderSDO().getCode())
                    .build();

            Map<String, String> map = new HashMap<>();
            map.put("name", loginUser.getRealName());

            map.put("active", EnumUtils.getAllocateApplyOrderOperationTypeEnum(operateType.getOperateType()));

            map.put("bizNo", fresh.getAllocateOrderSDO().getCode());

            map.put("orderType",EnumUtils.getAllocateOrderType(fresh.getAllocateOrderSDO().getType()));

            if (Objects.nonNull(fresh.getAllocateOrderSDO().getStatus())){
                AllocateOrderStatus allocateOrderStatus = AllocateOrderStatus.of(fresh.getAllocateOrderSDO().getStatus());
                if (Objects.nonNull(allocateOrderStatus)){
                    map.put("status",allocateOrderStatus.getDesc());
                }
            }
            String logInfo = JSONObject.toJSONString(map);
            opLogModel.setFeatures(logInfo);

            log.asyncMonitorObjectChangeV2(opLogModel);
        }catch (Throwable e){
            log.error("调拨单日志写入失败:" , e);
            //这里要吃掉异常，防止影响主流程
            // throw  e;
        }
    }

    /**
     * 调拨预报单日志写入
     * @param oldValue
     * @param newValue
     * @param operateType
     * @param features
     */
    private void saveAllocateApplyOrderDocumentLog(AllocateApplyOrderDetailDTO oldValue, AllocateApplyOrderDetailDTO newValue, Integer operateType, Map<String, String> features) {
        try {
            LoginUser loginUser = epochLoginUserService.getLoginUser();
            if (Objects.nonNull(oldValue)) {
                oldValue.setList(null);
            }
            if (Objects.nonNull(newValue)) {
                newValue.setList(null);
            }
            OpLogModel<AllocateApplyOrderDetailDTO> opLogModel = OpLogModel.<AllocateApplyOrderDetailDTO>builder()
                    .oldValue(oldValue)
                    .newValue(newValue)
                    .moduleName(ModuleEnum.UNIFIED_INVENTORY.getModuleName())
                    .moduleId(ModuleEnum.UNIFIED_INVENTORY.getModuleId())
                    .operateType(operateType)
                    .operatorType(OperatorType.USER.getOperatorType())
                    .operatorId(loginUser.getEmpId())//操作人
                    .objectId(newValue.getId().toString())
                    .objectName(newValue.getCode())
                    .features(JSONObject.toJSONString(features))
                    .build();
            log.asyncMonitorObjectChangeV2(opLogModel);
        }catch (Throwable e){
            log.error("调拨单日志写入失败:" , e);
            //这里要吃掉异常，防止影响主流程
        }
    }

    @Override
    @FacadeInvoker(value = "牛要客更新调拨单外部单号",errorCode = "OTS-03-005-00-15-086")
    public Result<List<NykSaleOrderRise>> nykUpdateoAllocateOrderOutOrderId(NykSaleOrderRiseList nykSaleOrderRiseList) {
        List<NykSaleOrderRise> nykSaleOrderRiseReturns = new ArrayList<>();
        if (Objects.isNull(nykSaleOrderRiseList)||CollectionUtils.isEmpty(nykSaleOrderRiseList.getList())){
            return Result.success(nykSaleOrderRiseReturns);
        }
        List<NykSaleOrderRise> nykSaleOrderRises = nykSaleOrderRiseList.getList();
        List<String> failedDatas = new ArrayList<>();
        List<BatchRequest> batchRequests = new ArrayList<>();
        for (NykSaleOrderRise nykSaleOrderRise : nykSaleOrderRises){
            //根据调拨单外部编号获取调拨单主单信息
            AllocateOrderDetailsQueryRequest allocateOrderDetailsQueryRequest = new AllocateOrderDetailsQueryRequest();
            allocateOrderDetailsQueryRequest.setOutAllocateOrderId(nykSaleOrderRise.getId());
            AllocateOrderSDO allocateOrderSDO = allocateOrderReadRepository.query(allocateOrderDetailsQueryRequest);
            if (Objects.nonNull(allocateOrderSDO)&&StringUtils.isNotBlank(nykSaleOrderRise.getVbakVbeln())) {
                allocateOrderSDO.setNykId(nykSaleOrderRise.getVbakVbeln());
                allocateOrderWriteRepository.updata(allocateOrderSDO);
                nykSaleOrderRiseReturns.add(nykSaleOrderRise);
            }else{
                failedDatas.add(nykSaleOrderRise.getId());
                BatchRequest batchRequest = new BatchRequest();
                batchRequest.setType(4);
                batchRequest.setSapCode(nykSaleOrderRise.getVbakVbeln());
                batchRequest.setOrderId(nykSaleOrderRise.getId());
                batchRequest.setFailureReason("没找到平台中牛要客数据");
                batchRequests.add(batchRequest);
            }
        }
        //保存失败的数据
        BatchCreateRequest batchCreateRequest =new BatchCreateRequest();
        batchCreateRequest.setBatchRequest(batchRequests);
        if (CollectionUtils.isNotEmpty(batchRequests)) {
            ouiNykFailRecordService.batchCreate(batchCreateRequest);
        }
        log.info("AllocateOrderWriteServiceImpl.nykUpdateoAllocateOrderOutOrderId,failedDatas:{}",JSONObject.toJSONString(failedDatas));
        return Result.success(nykSaleOrderRiseReturns);
    }

    /**
     * 订正数据后修正调拨单和明细的状态
     * @param allocateOrderFixStatusRequest
     * @return
     */
    @Override
    public Result<AllocateOrderFixStatusResponse> fixStatus(AllocateOrderFixStatusRequest allocateOrderFixStatusRequest) {
        AllocateOrderFixStatusResponse response = new AllocateOrderFixStatusResponse();
        if (Objects.isNull(allocateOrderFixStatusRequest) || StringUtils.isBlank(allocateOrderFixStatusRequest.getOutAllocateOrderIds())){
            return Result.success(response);
        }
        String[] idArray = allocateOrderFixStatusRequest.getOutAllocateOrderIds().split(",");
        if (idArray == null || idArray.length == 0){
            return Result.success(response);
        }
        Set<String> set = Stream.of(idArray).collect(Collectors.toSet());
        Set<String> resSet = Sets.newHashSet();
        for (String outAllocateOrderId : set) {
            if (StringUtils.isBlank(outAllocateOrderId)){
                continue;
            }
            try(RedisLock lock = new RedisLock(BcOrderConstants.REDIS_CACHE_CODE(),ALLOCATE_ORDER_LOCK_SYNC + outAllocateOrderId,5 * 60 * 1000,10 * 60 * 1000)) {
                if (!lock.lock()) {
                    throw new FacadeException("OTS-03-005-00-15-096");
                }
                boolean success = allocateOrderWriteRepository.fixStatus(outAllocateOrderId);
                if (success){
                    resSet.add(outAllocateOrderId);
                }
            }catch (Exception e){
                log.error("fixStatus",e);
                continue;
            }
        }
        if (CollectionUtils.isNotEmpty(resSet)){
            response.setOutAllocateOrderIds(resSet.stream().collect(Collectors.joining(",")));
        }
        return Result.success(response);
    }

    //修正调拨单的交货数量
    @Override
    public Result<AllocateOrderFixDeliveryQuantityResponse> fixDeliveryQuantity(AllocateOrderFixDeliveryQuantityRequest allocateOrderFixDeliveryQuantityRequest) {
        AllocateOrderFixDeliveryQuantityResponse response = new AllocateOrderFixDeliveryQuantityResponse();
        if (Objects.isNull(allocateOrderFixDeliveryQuantityRequest) || StringUtils.isBlank(allocateOrderFixDeliveryQuantityRequest.getOutAllocateOrderIds())){
            return Result.success(response);
        }
        String[] idArray = allocateOrderFixDeliveryQuantityRequest.getOutAllocateOrderIds().split(",");
        if (idArray == null || idArray.length == 0){
            return Result.success(response);
        }
        Set<String> set = Stream.of(idArray).collect(Collectors.toSet());
        Set<String> resSet = Sets.newHashSet();
        for (String outAllocateOrderId : set) {
            if (StringUtils.isBlank(outAllocateOrderId)){
                continue;
            }
            try(RedisLock lock = new RedisLock(BcOrderConstants.REDIS_CACHE_CODE(),ALLOCATE_ORDER_LOCK_SYNC + outAllocateOrderId,5 * 60 * 1000,10 * 60 * 1000)) {
                if (!lock.lock()) {
                    throw new FacadeException("OTS-03-005-00-15-096");
                }
                boolean success = allocateOrderWriteRepository.fixDeliveryQuantity(outAllocateOrderId);
                if (success){
                    resSet.add(outAllocateOrderId);
                }
            }catch (Exception e){
                log.error("fixDeliveryQuantity",e);
                continue;
            }
        }
        if (CollectionUtils.isNotEmpty(resSet)){
            response.setOutAllocateOrderIds(resSet.stream().collect(Collectors.joining(",")));
        }
        return Result.success(response);
    }

    /**
     * 获取外部调拨单号
     * @param sdo 入参
     * @return 外部调拨单号
     */
    private String getOutAllocateOrderId(AllocateOrderWithOrderDetailsSDO sdo) {
        if (Objects.isNull(sdo) || Objects.isNull(sdo.getAllocateOrderSDO())) {
            return null;
        }
        return sdo.getAllocateOrderSDO().getOutAllocateOrderId();
    }

    @Override
    public Result handleBusinessScenarioHisData() {
        QueryListResponse listResult = allocateOrderService.queryList(QueryListRequest.builder().start(1).limit(1).type(AllocateOrderTypeEnum.HEADQUARTER_SALES.getType()).build());
        int total =listResult.getTotal();
        int pageSize = 1000;
        int pageTotalNum = total / pageSize + 1;
        log.info("handleBusinessScenarioHisData step 1  pageTotalNum:{}, pageSize:{} total:{}",pageTotalNum,pageSize,total);

        for (int i = 1; i <= pageTotalNum; i++) {
            log.info("handleBusinessScenarioHisData step 2 pageNum:{}",i);
            int start = (i-1)* pageSize;
            List result = allocateOrderService.queryList(QueryListRequest.builder().start(start).limit(pageSize).type(AllocateOrderTypeEnum.HEADQUARTER_SALES.getType()).build()).getResult();
            result.stream().forEach(e->{

                AllocateOrder allocateOrder = JSONObject.parseObject(JSONObject.toJSONString(e), AllocateOrder.class);
                GetBusinessScenarioResponse businessScenarioRes = allocateOrderReadService.getBusinessScenario(allocateOrder);
                String businessScenario =businessScenarioRes.getBusinessScenario();
                if (StringUtils.isNotEmpty(businessScenario)){
                    allocateOrder.setBusinessScenario(businessScenario);
                    // 更新调拨单
                    Object update = allocateOrderService.update(UpdateRequest.builder().id(allocateOrder.getId().toString()).businessScenario(businessScenario).build());
                    log.info(JSONObject.toJSONString(update));
                }

            });
        }

        return Result.success("处理成功");
    }

    @Override
    public AllocateOrderCloseResponse closeAllocateOrder(AllocateOrderCloseRequest allocateOrderCloseRequest) {
        log.info("AllocateOrderWriteService|closeAllocateOrder|enter|request={}", JSON.toJSONString(allocateOrderCloseRequest));
        List<AllocateOrderCloseDTO> resultList = new ArrayList<>();
        List<AllocateOrderCloseDTO> closeList = allocateOrderCloseRequest.getCloseList();
        if (CollectionUtils.isNotEmpty(closeList)) {
            // 根据关闭原因分组
//            Map<String, List<AllocateOrderCloseDTO>> closeCodeMap = closeList.stream().collect(Collectors.groupingBy(AllocateOrderCloseDTO::getCloseCode));
            Map<String, List<AllocateOrderCloseDTO>> closeCodeMap = Maps.newHashMap();
            closeList.forEach(allocateOrderCloseDTO -> {
                if (closeCodeMap.containsKey(allocateOrderCloseDTO.getCloseCode())) {
                    closeCodeMap.get(allocateOrderCloseDTO.getCloseCode()).add(allocateOrderCloseDTO);
                }else {
                    closeCodeMap.put(allocateOrderCloseDTO.getCloseCode(),Lists.newArrayList(allocateOrderCloseDTO));
                }
            });
            for (Map.Entry<String, List<AllocateOrderCloseDTO>> entry : closeCodeMap.entrySet()) {
                // 再根据调拨单明细关闭前的状态分组
                Map<Integer, List<AllocateOrderCloseDTO>> statusDetailMap = entry.getValue().stream().collect(Collectors.groupingBy(AllocateOrderCloseDTO::getStatus));

                for (Map.Entry<Integer, List<AllocateOrderCloseDTO>> statusEntry : statusDetailMap.entrySet()) {
                    List<AllocateOrderCloseDTO> toCloseDetails = statusEntry.getValue();
                    BatchCloseRequest closeRequest = new BatchCloseRequest();
                    if (StringUtils.isNotBlank(entry.getKey())) {
                        closeRequest.setCloseCode(entry.getKey());
                        ReasonForRejectionEnum reason = EnumUtils.getReasonForRejectionByCode(entry.getKey());
                        if (reason != null) {
                            closeRequest.setCloseReason(reason.getDesc());
                        }
                    }
                    closeRequest.setClose(true);
                    closeRequest.setOperatorId(allocateOrderCloseRequest.getOperatorId());

                    List<Long> detailIdList = toCloseDetails.stream().map(AllocateOrderCloseDTO::getDetailId).distinct().collect(Collectors.toList());
                    closeRequest.setIdList(detailIdList);
                    closeRequest.setStatus(UnifiedInventoryKey.PARTIAL_SHIPPED_TO_SHIPPED_MAP.getOrDefault(statusEntry.getKey(),AllocateOrderStatus.Cancel.getValue()));
                    log.info("closeAllocateOrder_closeRequest:{}",JSON.toJSONString(closeRequest));
                    Boolean closeResult = closeAllocateOrderList(closeRequest);
                    if (Boolean.TRUE.equals(closeResult)) {
                        toCloseDetails.forEach(dto -> dto.setSuccess(Boolean.TRUE));
                    } else {
                        toCloseDetails.forEach(dto -> {
                            dto.setSuccess(Boolean.FALSE);
                            dto.setErrorCode("OTS-05-001-10-17-012");
                            dto.setErrorMessage("更新明细行失败");
                        });
                    }
                    resultList.addAll(toCloseDetails);
                }
            }
        }

        // 发送关闭消息
        Set<AllocateOrderCloseDTO> closedSet = resultList.stream().filter(Objects::nonNull).filter(detail -> Boolean.TRUE.equals(detail.getSuccess())).collect(Collectors.toSet());
        if (CollectionUtils.isNotEmpty(closedSet)) {

            for (AllocateOrderCloseDTO dto : closedSet) {
                Map<String,String> map = new HashMap<>();
                map.put("_CZ_KEY", BcOrderConstants.TENANT_ID());
                try {
                    String messageKey = dto.getTransferOrderCode() + "@@" + dto.getDetailNo();
                    AllocateOrderWithOrderDetailsSDO message = new AllocateOrderWithOrderDetailsSDO();
                    AllocateOrderSDO allocateOrderSDO = new AllocateOrderSDO();
                    allocateOrderSDO.setOutAllocateOrderId(dto.getOutAllocateOrderId());
                    allocateOrderSDO.setCode(dto.getTransferOrderCode());
                    allocateOrderSDO.setUpdater(dto.getOperatorId());
                    message.setAllocateOrderSDO(allocateOrderSDO);
                    AllocateOrderDetailSDO detailSDO = new AllocateOrderDetailSDO();
                    detailSDO.setClose(Boolean.TRUE);
                    detailSDO.setLineNo(dto.getDetailNo());
                    detailSDO.setOutAllocateOrderLineId(dto.getOutAllocateOrderLineNo());
                    List<AllocateOrderDetailSDO> detailSDOS = new ArrayList<>();
                    detailSDOS.add(detailSDO);
                    message.setAllocateOrderDetailSDOS(detailSDOS);
                    boolean sendResult = epochMessageService.sendMessage("update_allocate_order_message","CZ_ALLOCATE_V2","ALLOCATE_ORDER_UPDATE",messageKey, JSON.toJSONString(message), map);
                    log.info("AllocateOrderWriteServiceImpl|closeAllocateOrder|sendMessage|sendResult={}", sendResult);
                } catch (Throwable t) {
                    log.error("AllocateOrderWriteServiceImpl|closeAllocateOrder|sendMessage|error|dto={}", JSON.toJSONString(dto), t);
                }
            }
        }
        return new AllocateOrderCloseResponse(resultList);
    }

    private Boolean closeAllocateOrderList(BatchCloseRequest closeRequest) {
        try {
            allocateOrderWriteRepository.batchCloseAllocateOrderDetail(closeRequest);
        } catch (Throwable e) {
            log.error("AllocateOrderWriteService|closeAllocateOrderList|error|request={}", JSON.toJSONString(closeRequest), e);
            log.error(e,"AllocateOrderWriteService_closeAllocateOrderList_request:{}",JSON.toJSONString(closeRequest));
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    /**
     *  sap008同步调拨单 创建/更新/关闭, F-F1, F1-F1货场/仓间调拨 以及各个对应的退单
     */
    @Override
    public Result<AllocateOrderSyncResponse> syncAllocateOrder(AllocateOrderSyncRequest allocateOrderSyncRequest) {
        log.info("syncAllocateOrder_request:{}",JSON.toJSONString(allocateOrderSyncRequest));
        /**
         *  加锁
         *  判断是新增 / 修改
         *  仓库是否有在试点范围内
         *  是否退单
         *  要发什么履约单 判断是否需要创建正向履约单
         *
         */
        AllocateOrderWithOrderDetailsSDO sdoOld = new AllocateOrderWithOrderDetailsSDO();

        AllocateOrderSyncResponse allocateOrderSyncResponse = null;
        Result<AllocateApplyOrderDetailDTO> allocateApplyOrderDetailDTOOld = new Result<>();
        AllocateOrderRequest allocateOrderRequest = new AllocateOrderRequest();
        com.alibaba.citrus.cr.unified.inventory.model.AllocateOrderEntity originAllocateOrderEntity;
        com.alibaba.citrus.cr.unified.inventory.model.AllocateOrderEntity currentAllocateOrderEntity;
        try(RedisLock lock = new RedisLock(CrUnifiedInventoryConstants.REDIS_CACHE_CODE(),ALLOCATE_ORDER_LOCK_SYNC + allocateOrderSyncRequest.getOutAllocateOrderId(),5 * 60 * 1000,10 * 60 * 1000)){
            if (!lock.lock()){
                throw new FacadeException("OTS-03-005-00-15-097");
            }
            if (Objects.nonNull(allocateOrderSyncRequest.getAllocateApplyOrderCode())) {
                QueryAllocateApplyOrderRequest queryAllocateApplyOrderRequest = new QueryAllocateApplyOrderRequest();
                queryAllocateApplyOrderRequest.setCode(allocateOrderSyncRequest.getAllocateApplyOrderCode());
                allocateApplyOrderDetailDTOOld = allocateApplyOrderReadService.queryAllocateApplyOrderByCode(queryAllocateApplyOrderRequest);
            }
            if (StringUtils.isNotEmpty(allocateOrderSyncRequest.getCode())||StringUtils.isNotEmpty(allocateOrderSyncRequest.getOutAllocateOrderId())) {
                allocateOrderRequest.setAllocateOrderCode(allocateOrderSyncRequest.getCode());
                allocateOrderRequest.setOutAllocateOrderId(allocateOrderSyncRequest.getOutAllocateOrderId());
                sdoOld = allocateOrderReadRepository.get(allocateOrderRequest);
            }
            AllocateOrderEntityComposeRequest composeRequest = AllocateOrderEntityComposeRequest.builder()
                    .outAllocateOrderId(allocateOrderSyncRequest.getOutAllocateOrderId())
                    .code(allocateOrderSyncRequest.getCode()).build();
            originAllocateOrderEntity = allocateOrderEntityFactory.composeAllocateOrderEntity(composeRequest);
            //校验redis时间戳
            verifyRequestTime(allocateOrderSyncRequest.getOutAllocateOrderId(),allocateOrderSyncRequest.getExt());
            //调用实际逻辑
            //这里判断下 运输方式, 自己放入货场仓
            loadTransferWarehouseId(allocateOrderSyncRequest);
            allocateOrderSyncResponse = allocateOrderWriteRepository.syncAllocateOrder(allocateOrderSyncRequest);

            //数量 状态可能有变动, 更新调拨单的状态.
            if (allocateOrderSyncResponse != null && StringUtils.isNotBlank(allocateOrderSyncResponse.getCode())
                    && allocateOrderSyncResponse.getCode().startsWith("STO")){
                //如果此次有关闭状态就不在这里操作状态更新.
                boolean noneMatchClosed = allocateOrderSyncRequest.getAllocateOrderScItemDetails().stream().noneMatch(detailDTO -> Boolean.TRUE.equals(detailDTO.getClosed()));
                if (noneMatchClosed){
                    UpdateStatusRequest updateStatusRequest = UpdateStatusRequest.builder().code(allocateOrderSyncResponse.getCode()).build();
                    allocateOrderDomainService.updateStatus(updateStatusRequest);
                }
            }

            //写缓存
            setCache(allocateOrderSyncResponse,allocateOrderSyncRequest);
            //写日志
            //发调拨单创建消息 暂未使用到


        }
        if (StringUtils.isEmpty(allocateOrderSyncRequest.getCode())) {
            allocateOrderRequest.setAllocateOrderCode(allocateOrderSyncResponse.getCode());
        }
        AllocateOrderWithOrderDetailsSDO sdoNew = allocateOrderReadRepository.get(allocateOrderRequest);
        if (StringUtils.isNotEmpty(allocateOrderSyncRequest.getCode())) {
            log.info("008接口记录日志更新方法：旧值：{}，新值:{}",JSONObject.toJSONString(sdoOld),JSONObject.toJSONString(sdoNew));
            if (allocateOrderSyncResponse != null && StringUtils.isNotBlank(allocateOrderSyncResponse.getCode())
                    && allocateOrderSyncResponse.getCode().startsWith("STO")){
                saveDocumentLog(sdoOld,sdoNew==null?sdoOld:sdoNew, AllocateApplyOrderOperationTypeEnum.CHANGE, OperatorType.USER.getOperatorType(),null);
            }
        }else{
            log.info("008接口记录日志创建方法：旧值：{}，新值:{}",JSONObject.toJSONString(sdoOld),JSONObject.toJSONString(sdoNew));
            if (allocateOrderSyncResponse != null && StringUtils.isNotBlank(allocateOrderSyncResponse.getCode())
                    && allocateOrderSyncResponse.getCode().startsWith("STO")){
                saveDocumentLog(sdoOld,sdoNew==null?sdoOld:sdoNew, AllocateApplyOrderOperationTypeEnum.CREATE, OperatorType.USER.getOperatorType(),null);
            }
        }
        AllocateOrderEntityComposeRequest composeRequest = AllocateOrderEntityComposeRequest.builder()
                .outAllocateOrderId(allocateOrderSyncRequest.getOutAllocateOrderId()).build();
        currentAllocateOrderEntity = allocateOrderEntityFactory.composeAllocateOrderEntity(composeRequest);

        Result<AllocateApplyOrderDetailDTO> allocateApplyOrderDetailDTONew = new Result<>();
        if (StringUtils.isNotBlank(allocateOrderSyncRequest.getAllocateApplyOrderCode())) {
            QueryAllocateApplyOrderRequest queryAllocateApplyOrderRequest = new QueryAllocateApplyOrderRequest();
            queryAllocateApplyOrderRequest.setCode(allocateOrderSyncRequest.getAllocateApplyOrderCode());
            allocateApplyOrderDetailDTONew = allocateApplyOrderReadService.queryAllocateApplyOrderByCode(queryAllocateApplyOrderRequest);
        }
        //添加调拨预报单单据日志
        if (StringUtils.isNotBlank(allocateOrderSyncRequest.getAllocateApplyOrderCode())) {
            Map<String, String> features = new HashMap<>();
            features.put(CODE, allocateOrderSyncRequest.getAllocateApplyOrderCode());
            if (allocateApplyOrderDetailDTONew.getResult() != null &&
                    allocateApplyOrderDetailDTONew.getResult().getStatus() != null){
                features.put(STATUS, allocateApplyOrderDetailDTONew.getResult().getStatus().toString());
            }
//            features.put(STATUS, allocateApplyOrderDetailDTONew.getResult().getStatus().toString());
            AllocateApplyOrderDetailDTO allocateApplyOrderDetailOld = allocateApplyOrderDetailDTOOld.getResult();
            AllocateApplyOrderDetailDTO allocateApplyOrderDetailNew = allocateApplyOrderDetailDTONew.getResult();
            saveAllocateApplyOrderDocumentLog(allocateApplyOrderDetailOld, allocateApplyOrderDetailNew, Integer.valueOf(AllocateApplyOrderOperationTypeEnum.UPDATE.getValue()), features);
        }
        try{
            if (allocateOrderSyncResponse != null && StringUtils.isNotBlank(allocateOrderSyncResponse.getCode())
                    && allocateOrderSyncResponse.getCode().startsWith("STO")){
//                AllocateOrderDetailsQueryRequest queryRequest = new AllocateOrderDetailsQueryRequest();
//                queryRequest.setCode(allocateOrderSyncResponse.getCode());
//                Result<AllocateOrderDetailsQueryResponse> query = allocateOrderReadService.query(queryRequest);
                AllocateOrderGetMessageRequest allocateOrderGetMessageRequest = new AllocateOrderGetMessageRequest();
                allocateOrderGetMessageRequest.setCode(allocateOrderSyncResponse.getCode());
                Result<AllocateOrderMessageResponse> allocateOrderMessage = allocateOrderReadService.getAllocateOrderMessage(allocateOrderGetMessageRequest);
                Map<String,String> map = Maps.newHashMap();
                map.put("_CZ_KEY", BcOrderConstants.TENANT_ID());
                epochMessageService.sendMessage("allocate_order_message","CZ_ALLOCATE_V2","AllocateCreate",
                        allocateOrderSyncResponse.getCode(),JSON.toJSONString(allocateOrderMessage.getResult()),map);
            }
        }catch(ErrorCodeException e){
            log.error(e,"AllocateOrderWriteServiceImpl.passiveSync exception,code:{},msg:{}",e.getErrorCode(),e.getErrorMessage());
            return Result.fail(e.getErrorCode(), e.getErrorMessage());
        }catch (Throwable e){
            log.error("AllocateOrderWriteServiceImpl.passiveSync exception:",e);
            // 异常在FacadeInvokeHandler中会处理
            throw e;
        }
        //创建履约单
        try{
            Map<String,String> map = Maps.newHashMap();
            map.put("_CZ_KEY", BcOrderConstants.TENANT_ID());
            AllocateOrderSapSyncMessageEntity sapSyncMessage = new AllocateOrderSapSyncMessageEntity();
            sapSyncMessage.setOrigin(originAllocateOrderEntity);
            sapSyncMessage.setCurrent(currentAllocateOrderEntity);
            sapSyncMessage.setSyncRequest(allocateOrderSyncRequest);
            epochMessageService.sendMessage("sap_sync_message","CZ_ALLOCATE_V2","sapSync",
                    currentAllocateOrderEntity.getAllocateOrder().getCode(),JSON.toJSONString(sapSyncMessage),map);
        }catch (Throwable e){
            log.error("sap_sync_message_send_error",e);
        }
        //008回复时发送修改消息
        try{
            AllocateOrderLoadListRequest allocateOrderLoadListRequest = new AllocateOrderLoadListRequest();
            if (StringUtils.isNotBlank(allocateOrderSyncRequest.getCode())){
                allocateOrderLoadListRequest.setCode(allocateOrderSyncRequest.getCode());
            }
            if (StringUtils.isNotBlank(allocateOrderSyncRequest.getOutAllocateOrderId())){
                allocateOrderLoadListRequest.setOutAllocateOrderId(allocateOrderSyncRequest.getOutAllocateOrderId());
            }
            Result<List<AllocateOrder>> listResult = allocateOrderService.loadAllocateOrderList(allocateOrderLoadListRequest);
            if (listResult != null && CollectionUtils.isNotEmpty(listResult.getResult())){
                AllocateOrder allocateOrder = listResult.getResult().get(0);
                AllocateOrderStandardMessageDTO messageDTO = AllocateOrderStandardMessageDTO.builder()
                        .keyword(allocateOrder.getCode())
                        .eventCode(MessageEventCodeEnum.EDIT.getCode())
                        .businessCode(MessageBusinessCodeEnum.ALLOCATE_ORDER.getCode())
                        .businessType(allocateOrder.getBusinessType())
                        .fromSys(allocateOrder.getFromSys())
                        .eventTime(new Date())
                        .build();
                allocateOrderStandardMessageService.sendAllocateOrderStandardMessage(messageDTO);
            }
        }catch (Throwable e){
            log.error("syncAllocateOrder_standardMessage_error",e);
        }


//        handleFulfillmentOrder(originAllocateOrderEntity,currentAllocateOrderEntity,allocateOrderSyncRequest);
        return Result.success(allocateOrderSyncResponse);
    }

    private void verifyRequestTime(String key,String requestTime){
        //判断时间戳 如果时间
        String modifierTime = epochCacheService.get(BcOrderConstants.REDIS_CACHE_CODE(),
                ALLOCATE_ORDER_CONFIRM_QUANTITY_KEY + key);
        //下发时间戳
        if (StringUtils.isNotBlank(modifierTime)){
            String[] modifierTimeArray = modifierTime.split("\\.");
            String[] requestTimeArray = requestTime.split("\\.");
            if (Long.parseLong(modifierTimeArray[0]) > Long.parseLong(requestTimeArray[0])){
                throw new FacadeException("OTS-03-005-00-15-200");
            }else if (Long.valueOf(modifierTimeArray[0]).longValue() == Long.valueOf(requestTimeArray[0]).longValue()) {
                if (Long.parseLong(modifierTimeArray[1]) >= Long.parseLong(requestTimeArray[1])){
                    throw new FacadeException("OTS-03-005-00-15-200");
                }
            }
        }
    }
    private void setCache(AllocateOrderSyncResponse response,AllocateOrderSyncRequest request){
        if (Objects.nonNull(response) && StringUtils.isNotBlank(response.getCode()) && response.getCode().startsWith("STO")){
            epochCacheService.put(BcOrderConstants.REDIS_CACHE_CODE(),
                    ALLOCATE_ORDER_CONFIRM_QUANTITY_KEY + request.getOutAllocateOrderId(),
                    request.getExt(), SAP_TIMESTAMP_CACHE_EXPIRE, TimeUnit.SECONDS);
        }
    }

    private void sendAllocateOrderCreateMessage(AllocateOrderResponse allocateOrderSyncResponse){
        try{
            if (allocateOrderSyncResponse != null && StringUtils.isNotBlank(allocateOrderSyncResponse.getCode())
                    && allocateOrderSyncResponse.getCode().startsWith("STO")){
                AllocateOrderGetMessageRequest allocateOrderGetMessageRequest = new AllocateOrderGetMessageRequest();
                allocateOrderGetMessageRequest.setCode(allocateOrderSyncResponse.getCode());
                Result<AllocateOrderMessageResponse> allocateOrderMessage = allocateOrderReadService.getAllocateOrderMessage(allocateOrderGetMessageRequest);
                Map<String,String> map = Maps.newHashMap();
                map.put("_CZ_KEY", BcOrderConstants.TENANT_ID());
                epochMessageService.sendMessage("allocate_order_message","CZ_ALLOCATE_V2","AllocateCreate",
                        allocateOrderSyncResponse.getCode(),JSON.toJSONString(allocateOrderMessage.getResult()),map);
            }
        }catch(ErrorCodeException e){
            log.error(e,"AllocateOrderWriteServiceImpl.passiveSync exception,code:{},msg:{}",e.getErrorCode(),e.getErrorMessage());
            throw e;
        }catch (Throwable e){
            log.error("AllocateOrderWriteServiceImpl.passiveSync exception:",e);
            // 异常在FacadeInvokeHandler中会处理
            throw e;
        }
    }
    private void loadTransferWarehouseId(AllocateOrderSyncRequest syncRequest){
        if (Arrays.asList(AllocateOrderShipTypeEnums.TRAIN,AllocateOrderShipTypeEnums.SEA).stream()
                .map(AllocateOrderShipTypeEnums::getType).anyMatch(type -> type.equals(syncRequest.getShipMethod()))){
            if (StringUtils.isBlank(syncRequest.getReceiveAreaId())){
                return;
            }
            com.alibaba.cloud.retail.store.request.warehouse.WarehouseQueryRequest warehouseQueryRequest = new WarehouseQueryRequest();
            warehouseQueryRequest.setWarehouseIds(com.google.common.collect.Lists.newArrayList(syncRequest.getReceiveAreaId()));
            com.alibaba.cz.base.tool.result.Result<List<com.alibaba.cz.base.model.store.WarehouseSDO>> listResult = warehouseQueryService.batchQueryList(warehouseQueryRequest);
            List<WarehouseSDO> warehouseSDOList = Optional.ofNullable(listResult).map(com.alibaba.cz.base.tool.result.Result::getResult)
                    .orElse(com.google.common.collect.Lists.newArrayList());
            log.info("loadTransferWarehouseId_result:{}",JSON.toJSONString(warehouseSDOList));
            if (CollectionUtils.isNotEmpty(warehouseSDOList)){
                Map<String, String> feature = warehouseSDOList.get(0).getBizFeatures();
                if (MapUtils.isNotEmpty(feature)){
                    syncRequest.setTransferWarehouseId(feature.get(TRANSFER_WAREHOUSE_ID));
                }
            }
        }
    }

    private void createForwardOrder(AllocateOrder allocateOrder,
                                    List<AllocateOrderDetail> allocateOrderDetailList,
                                    Map<String,String> map){
        CreateFulfillmentOrderReq req = new CreateFulfillmentOrderReq();
        if (StringUtils.isNotBlank(allocateOrder.getFromSys())){
            req.setFromSys(allocateOrder.getFromSys());
        }else {
            req.setFromSys("APP_SAP");
        }
        req.setBusinessType(StringUtils.isNotBlank(allocateOrder.getBusinessType()) ? allocateOrder.getBusinessType() : "11");
        //TODO 业务单据编码
        req.setBusinessUnitCode("6000");
        req.setRequestNo(UUID.randomUUID().toString().replaceAll("-",""));
        //调拨单号
        req.setSourceOrderNo(allocateOrder.getCode());
        //退单:调拨退单出库,  否:调拨出库
        if (Boolean.TRUE.equals(allocateOrder.getReturnOrder())){
            req.setRefOrderType(OrderTypeEnum.ALLOCATE_RETURN_OUTBOUND.getValue());
        }else {
            req.setRefOrderType(OrderTypeEnum.ALLOCATE_OUTBOUND.getValue());
        }
        //调出仓和调入仓属于同一个物理仓（虚拟移库） 更正逻辑改为如果调拨单创建时履约类型为虚拟移库,则这里传递为虚拟移库
//        if (Objects.equals(allocateOrder.getShipWarehouseId(),allocateOrder.getReceiveWarehouseId())){
        if (com.epoch.app.crunifiedinventory.model.enums.FulfillmentTypeEnum.VIRTUAL_TRANSFER.getValue().toString().equals(allocateOrder.getFulfillmentTypeCode())){
            req.setFulfillmentType(FulfillmentTypeEnum.EXTERNALWAREHOUSE_FULFILLMENT.getValue());
        }else {
            req.setFulfillmentType(FulfillmentTypeEnum.BIGWAREHOUSE_FULFILLMENT.getValue());
        }
        req.setAutoSubmit(Boolean.TRUE);
        //枚举：：1=普通，100=优先
        req.setPriority(allocateOrder.getPriority() == null ? "1" : allocateOrder.getPriority().toString());
        req.setProductGroupCode(allocateOrder.getProductGroup());
        Map<String, CargoZoneSDO> cargoZoneSDOMap = this.getCargoZoneSDOMap(Arrays.asList(allocateOrder.getShipAreaId(), allocateOrder.getReceiveAreaId(), allocateOrder.getShipWarehouseId(), allocateOrder.getReceiveWarehouseId()).stream().filter(StringUtils::isNotBlank)
                .collect(Collectors.toList()));
        Map<String, CargoZoneSDO> codeZoneSDOMap = this.getCargoZoneSDOMapByCodes(Arrays.asList(allocateOrder.getTransferWarehouseId()).stream().filter(StringUtils::isNotBlank).collect(Collectors.toList()));
        String logicShippingWarehouseCode = Optional.ofNullable(cargoZoneSDOMap.get(allocateOrder.getShipAreaId())).map(CargoZoneSDO::getCode).orElse(null);
        String logicShippingWarehouseName = Optional.ofNullable(cargoZoneSDOMap.get(allocateOrder.getShipAreaId())).map(CargoZoneSDO::getName).orElse(null);
        String shippingWarehouseCode = Optional.ofNullable(cargoZoneSDOMap.get(allocateOrder.getShipWarehouseId())).map(CargoZoneSDO::getCode).orElse(null);
        String shippingWarehouseName = Optional.ofNullable(cargoZoneSDOMap.get(allocateOrder.getShipWarehouseId())).map(CargoZoneSDO::getName).orElse(null);
        String logicReceivingWarehouseCode = Optional.ofNullable(cargoZoneSDOMap.get(allocateOrder.getReceiveAreaId())).map(CargoZoneSDO::getCode).orElse(null);
        String logicReceivingWarehouseName = Optional.ofNullable(cargoZoneSDOMap.get(allocateOrder.getReceiveAreaId())).map(CargoZoneSDO::getName).orElse(null);
        String receivingWarehouseCode = Optional.ofNullable(cargoZoneSDOMap.get(allocateOrder.getReceiveWarehouseId())).map(CargoZoneSDO::getCode).orElse(null);
        String receivingWarehouseName = Optional.ofNullable(cargoZoneSDOMap.get(allocateOrder.getReceiveWarehouseId())).map(CargoZoneSDO::getName).orElse(null);
        String transferWarehouseName = Optional.ofNullable(codeZoneSDOMap.get(allocateOrder.getTransferWarehouseId())).map(CargoZoneSDO::getName).orElse(null);
        Map<String,String> features = Maps.newHashMap();
        features.put(FeaturesKey.OUT_ALLOCATE_ORDER_ID,allocateOrder.getOutAllocateOrderId());
        if (Boolean.TRUE.equals(allocateOrder.getReturnOrder())){
            req.setLogicReceivingWarehouseCode(logicShippingWarehouseCode);
            req.setLogicReceivingWarehouse(logicShippingWarehouseName);
            req.setReceivingWarehouseCode(shippingWarehouseCode);
            req.setReceivingWarehouse(shippingWarehouseName);
            features.put(FeaturesKey.TRANSFER_ORDER_TARGET_LOGIC_WAREHOUSE_CODE,shippingWarehouseCode);
            if (ToolUtil.isTrainOrSea(allocateOrder.getShipMethod())){
                req.setLogicShippingWarehouseCode(allocateOrder.getTransferWarehouseId());
                req.setLogicShippingWarehouse(transferWarehouseName);
            }else {
                req.setLogicShippingWarehouseCode(logicReceivingWarehouseCode);
                req.setLogicShippingWarehouse(logicReceivingWarehouseName);
                req.setShippingWarehouseCode(receivingWarehouseCode);
                req.setShippingWarehouse(receivingWarehouseName);
            }
            CargoZoneSDO cargoZoneSDO = cargoZoneSDOMap.get(allocateOrder.getReceiveAreaId());
            if (cargoZoneSDO != null && cargoZoneSDO.getFeature() != null){
                req.setSoldToPartyCode(cargoZoneSDO.getFeature().get("sapInternalSalesBPCode"));
                req.setShipToPartyCode(cargoZoneSDO.getFeature().get("sapInternalDeliveryBPCode"));
            }
        }else {
            req.setLogicShippingWarehouseCode(logicShippingWarehouseCode);
            req.setLogicShippingWarehouse(logicShippingWarehouseName);
            req.setShippingWarehouseCode(shippingWarehouseCode);
            req.setShippingWarehouse(shippingWarehouseName);
            features.put(FeaturesKey.TRANSFER_ORDER_TARGET_LOGIC_WAREHOUSE_CODE,logicReceivingWarehouseCode);
            if (ToolUtil.isTrainOrSea(allocateOrder.getShipMethod())){
                req.setLogicReceivingWarehouseCode(allocateOrder.getTransferWarehouseId());
                req.setLogicReceivingWarehouse(transferWarehouseName);
                CargoZoneSDO cargoZoneSDO = cargoZoneSDOMap.get(allocateOrder.getReceiveAreaId());
                if (cargoZoneSDO != null && MapUtils.isNotEmpty(cargoZoneSDO.getFeature())){
                    req.setSoldToPartyCode(cargoZoneSDO.getFeature().get("sapInternalSalesBPCode"));
                    req.setShipToPartyCode(cargoZoneSDO.getFeature().get("sapInternalDeliveryBPCode"));
                }
            }else {
                req.setLogicReceivingWarehouseCode(logicReceivingWarehouseCode);
                req.setLogicReceivingWarehouse(logicReceivingWarehouseName);
                req.setReceivingWarehouseCode(receivingWarehouseCode);
                req.setReceivingWarehouse(receivingWarehouseName);
                //客户售达方编码 来自于仓库上的售达方编码
                CargoZoneSDO cargoZoneSDO = cargoZoneSDOMap.get(allocateOrder.getReceiveAreaId());
                if (cargoZoneSDO != null && MapUtils.isNotEmpty(cargoZoneSDO.getFeature())){
                    req.setSoldToPartyCode(cargoZoneSDO.getFeature().get("sapInternalSalesBPCode"));
                    req.setShipToPartyCode(cargoZoneSDO.getFeature().get("sapInternalDeliveryBPCode"));
                }
            }
        }
        if (MapUtils.isNotEmpty(features)){
            req.setFeatures(FeatureUtil.toString(features));
        }
        req.setProductGroupCode(StringUtils.isNotBlank(allocateOrder.getProductGroup()) ? allocateOrder.getProductGroup() : "11");
        req.setChannelCode("90");
        //6000?
        req.setSalesOrganizationCode("6000");
        //TODO 送达方 售达方 选择仓库上的, 待定
//            req.setShipToPartyCode();
//        req.setCreator(allocateOrder.getCreator());
        req.setExpectedDeliveryTime(Objects.isNull(allocateOrder.getExpectedDeliveryDate()) ? null :
                DateFormatUtil.toString(allocateOrder.getExpectedDeliveryDate(),DateFormatUtil.TIME_PATTERN));
        req.setShippingMethodCode(allocateOrder.getShipMethod() == null ? null : allocateOrder.getShipMethod().toString());
        req.setRemark(allocateOrder.getRemark());

        List<CreateFulfillmentOrderDetailDTO> detailDTOList = Lists.newArrayList();
        allocateOrderDetailList.forEach(detail -> {
            CreateFulfillmentOrderDetailDTO detailDTO = new CreateFulfillmentOrderDetailDTO();
            detailDTO.setSourceOrderLineNo(detail.getLineNo() == null ? null : detail.getLineNo().toString());
            detailDTO.setRefOrderNo(allocateOrder.getCode());
            detailDTO.setRefOrderLineNo(detail.getLineNo() == null ? null : detail.getLineNo().toString());
            detailDTO.setMaterialCode(detail.getScItemCode());
            detailDTO.setRefOrderLineNo(detail.getLineNo() == null ? null : detail.getLineNo().toString());
            detailDTO.setRefOrderCreateTime(allocateOrder.getGmt_create() == null ? null :
                    DateFormatUtil.toString(allocateOrder.getGmt_create(),"yyyy-MM-dd HH:mm:ss"));
            detailDTO.setProductionDate(detail.getExpectedProduceDate() == null ? null :
                    DateFormatUtil.toString(allocateOrder.getExpectedDeliveryDate(),"yyyy-MM-dd"));
            detailDTO.setSalesUnitCode(StringUtils.isBlank(detail.getUnit()) ? "PC" : detail.getUnit());
            detailDTO.setSaleQuantity(new BigDecimal(detail.getQuantity().trim()));
            detailDTO.setQuantity(new BigDecimal(detail.getQuantity().trim()));
            detailDTO.setShippingUnitCode(StringUtils.isBlank(detail.getUnit()) ? "PC" : detail.getUnit());
            detailDTO.setRemark(detail.getRemarks());
            detailDTO.setClose(Boolean.TRUE.equals(detail.getClose()) ? 1 : 0);
            detailDTO.setCloseReasonCode(detail.getCloseCode());
            detailDTO.setCloseReason(detail.getCloseReason());
            Map<String,String> featuresMap = Maps.newHashMap();
            if (StringUtils.isNotBlank(detail.getOutAllocateOrderLineId())){
                featuresMap.put(FeaturesKey.OUT_ALLOCATE_ORDER_LINE_ID,detail.getOutAllocateOrderLineId());
            }
            detailDTO.setFeatures(FeatureUtil.toString(featuresMap));
            detailDTOList.add(detailDTO);
        });
        req.setDetailList(detailDTOList);
        log.info("syncAllocateOrder_create_order_request:{}",JSON.toJSONString(req));
        com.alibaba.cloud.retail.delivery.api.model.delivery.response.Result<FulfillmentOrderResponse> order = fulfillmentOrderAbilityService.createOrder(req);
        log.info("syncAllocateOrder_create_order_response:{}",JSON.toJSONString(order));
        if (order != null && order.isSuccess()){
            map.put("forwardFulfillmentOrderNo",order.getResult().getFulfillmentOrderNo());
            allocateOrder.setFeatures(FeatureUtil.toString(map));
            allocateOrderService.updateAllocateOrder(allocateOrder);
        }
    }

    //TODO 联调临时用一下,后面再抽出来
    public Map<String,CargoZoneSDO> getCargoZoneSDOMap(List<String> idList){
        log.info("abc_request:{}",JSON.toJSONString(idList));
        Map<String,CargoZoneSDO> map = Maps.newHashMap();
        if (CollectionUtils.isEmpty(idList)){
            return map;
        }
        QueryCargoZoneRequest queryCargoZoneRequest = new QueryCargoZoneRequest();
        queryCargoZoneRequest.setWarehouseAreaIds(idList);
        queryCargoZoneRequest.setStart(0);
        queryCargoZoneRequest.setSize(idList.size());
        log.info("abcd_request:{}",JSON.toJSONString(queryCargoZoneRequest));
        com.alibaba.cz.base.tool.result.Result<ResultPager<List<CargoZoneSDO>>> resultPagerResult = cargoZoneQueryService.searchCargoZoneListByDB(queryCargoZoneRequest);
        log.info("abc_res:{}",JSON.toJSONString(resultPagerResult));
        Optional.ofNullable(resultPagerResult).map(com.alibaba.cz.base.tool.result.Result::getResult)
                .map(ResultPager::getContent).orElse(com.google.common.collect.Lists.newArrayList())
                .stream().forEach(cargoZoneSDO -> map.put(cargoZoneSDO.getId(),cargoZoneSDO));
        return map;
    }

    public Map<String,CargoZoneSDO> getCargoZoneSDOMapByCodes(List<String> codes){
        log.info("code_request:{}",JSON.toJSONString(codes));
        Map<String,CargoZoneSDO> map = Maps.newHashMap();
        if (CollectionUtils.isEmpty(codes)){
            return map;
        }
        QueryCargoZoneRequest queryCargoZoneRequest = new QueryCargoZoneRequest();
        queryCargoZoneRequest.setCodes(codes);
        queryCargoZoneRequest.setStart(0);
        queryCargoZoneRequest.setSize(codes.size());
        log.info("code_request:{}",JSON.toJSONString(queryCargoZoneRequest));
        com.alibaba.cz.base.tool.result.Result<ResultPager<List<CargoZoneSDO>>> resultPagerResult = cargoZoneQueryService.searchCargoZoneListByDB(queryCargoZoneRequest);
        log.info("code_res:{}",JSON.toJSONString(resultPagerResult));
        Optional.ofNullable(resultPagerResult).map(com.alibaba.cz.base.tool.result.Result::getResult)
                .map(ResultPager::getContent).orElse(com.google.common.collect.Lists.newArrayList())
                .stream().forEach(cargoZoneSDO -> map.put(cargoZoneSDO.getCode(),cargoZoneSDO));
        return map;
    }

    private AllocateOrderCloseDTO closeSingleAllocateOrder(AllocateOrderCloseDTO dto) {
        /**
         *     1. 子单中“是否关闭”字段记录为“是”，记录关闭原因编码（若有）以及关联查询关闭原因描述后保存。
         *     2. 推进子单状态以及主单状态，其中子单状态：
         *     一、 待发货推进到已取消。
         *     二、 部分发货，xxxx推进到已发货，xxxx。
         *     3. 异步同步SAP进行关闭（三方集成开发并发布完成前，不测试SAP关闭）。
         */
        com.epoch.app.crunifiedinventory.domain.allocateorderdetail.dto.AllocateOrderDetailLoadListRequest request = new com.epoch.app.crunifiedinventory.domain.allocateorderdetail.dto.AllocateOrderDetailLoadListRequest();
        request.setAllocateOrderCode(dto.getTransferOrderCode());
        request.setLineNo(Integer.valueOf(dto.getDetailNo()));

        Result<List<AllocateOrderDetail>> listResult = allocateOrderDetailService.loadAllocateOrderDetailList(request);
        if (CollectionUtils.isEmpty(listResult.getResult())) {
            dto.setSuccess(Boolean.FALSE);
            dto.setErrorCode("OTS-05-001-10-17-010");
            dto.setErrorMessage("没有查询到对应的明细行");
            return dto;
        }

        AllocateOrderDetail detail = listResult.getResult().get(0);
        detail.setClose(true);
        if (StringUtils.isNotBlank(dto.getCloseCode())) {
            detail.setCloseCode(dto.getCloseCode());
            ReasonForRejectionEnum reason = EnumUtils.getReasonForRejectionByCode(dto.getCloseCode());
            if (reason != null) {
                detail.setCloseReason(reason.getDesc());
            }
        }
        detail.setOperatorId(dto.getOperatorId());

        if (UnifiedInventoryKey.PARTIAL_SHIPPED_TO_SHIPPED_MAP.containsKey(detail.getStatus())) {
            detail.setStatus(UnifiedInventoryKey.PARTIAL_SHIPPED_TO_SHIPPED_MAP.get(detail.getStatus()));
        } else {
            dto.setSuccess(Boolean.FALSE);
            dto.setErrorCode("OTS-05-001-10-17-011");
            dto.setErrorMessage("明细行的状态应为待发货或部分发货");
            return dto;
        }
        UpdateAllocateOrderDetailResponse updateResponse = allocateOrderDetailService.updateAllocateOrderDetail(detail);

        if (updateResponse.getCount() == 1) {
            dto.setSuccess(Boolean.TRUE);

//            Map<String,String> map = new HashMap<>();
//            map.put("_CZ_KEY", BcOrderConstants.TENANT_ID());
//            try {
//                String messageKey = dto.getTransferOrderCode() + "@@" + dto.getDetailNo();
//                boolean sendResult = epochMessageService.sendMessage("close_allocate_order_message","CZ_ALLOCATE_V2","ALLOCATE_ORDER_DELETE", messageKey , JSON.toJSONString(dto), map);
//                log.info("AllocateOrderWriteServiceImpl|closeAllocateOrder|sendMessage|sendResult={}", sendResult);
//            } catch (Exception e) {
//                log.error("AllocateOrderWriteServiceImpl|closeAllocateOrder|sendMessage|error|dto={}", JSON.toJSONString(dto), e);
//            }
        } else {
            dto.setSuccess(Boolean.FALSE);
            dto.setErrorCode("OTS-05-001-10-17-012");
            dto.setErrorMessage("更新明细行失败");
        }

        return dto;
    }
    /** 008回复时, 根据确认数量变化 创建履约单 **/
    public void handleFulfillmentOrder(com.alibaba.citrus.cr.unified.inventory.model.AllocateOrderEntity origin,
                                       com.alibaba.citrus.cr.unified.inventory.model.AllocateOrderEntity current,
                                       AllocateOrderSyncRequest syncRequest){
        if (current == null || current.getAllocateOrder() == null || CollectionUtils.isEmpty(current.getDetailList())){
            log.info("handleFulfillmentOrder_pass");
            return;
        }
        createFulfillmentOrder(origin,current,syncRequest);
        try{
            closeFulfillmentOrderAndAllocateOrder(origin,current);
        }catch (Throwable e){
            log.error("AllocateOrderWriteServiceImpl_close_error",e);
        }
        //构建正向履约单的主单和明细
    }

    /**
     * 创建履约单(正向)
     * @param origin
     * @param current
     */
    private void createFulfillmentOrder(com.alibaba.citrus.cr.unified.inventory.model.AllocateOrderEntity origin,
                                        com.alibaba.citrus.cr.unified.inventory.model.AllocateOrderEntity current,
                                        AllocateOrderSyncRequest syncRequest){
        AllocateOrder currentAllocateOrder = current.getAllocateOrder();
        // 判断下 是否自动履约履约 且 是否是大厂履约
        /**
         * 008回复时能否创建履约单(无论正逆向)
         * F-F1 计划调拨 不创
         * F-F1 计划货场 不创
         * 货场提货  创 正向
         *
         *仓间调拨
         *  试点-试点  创 正向
         *  试点-外部  创 正向
         *  外部-试点 sap 创  不创
         *
         * 退单
         * F-F1 计划退单, 008创建 创调拨退单出库的; 正向
         * 仓间
         * 外部到-试点, 退单,  SAP创,  008创建 调拨退单出库 正向,
         * 试点-外部 退单  sap创, 008不能创, 等出库创逆向, 调拨退单入库.
         * 试点-试点, 退单 008创建调拨退单出库., 正向.
         */
        if (!Boolean.TRUE.equals(currentAllocateOrder.getAutoFulfillment()) ||
                Arrays.asList(com.epoch.app.crunifiedinventory.model.enums.FulfillmentTypeEnum.PILOT2CURRENT,
                                com.epoch.app.crunifiedinventory.model.enums.FulfillmentTypeEnum.PILOT2PILOT,
                                com.epoch.app.crunifiedinventory.model.enums.FulfillmentTypeEnum.VIRTUAL_TRANSFER).stream()
                        .noneMatch(fulfillmentTypeEnum -> fulfillmentTypeEnum.getValue().toString().equals(currentAllocateOrder.getFulfillmentTypeCode()))){
            log.info("no_need_create_forward_fulfillment_order:{}",JSON.toJSONString(current));
            return;
        }
        boolean isCreate = Objects.isNull(origin);
        //判断是否是新建, 注意要筛选出是确认数量
        if (isCreate){
            log.info("createFulfillmentOrder_isCreate");
            Map<String, String> bizFeaturesMap = FeatureUtil.fromString(currentAllocateOrder.getBizFeatures());
            createForwardOrder(currentAllocateOrder,current.getDetailList(),bizFeaturesMap);
        }else {
            log.info("createFulfillmentOrder_isUpdate");
            List<CreateFulfillmentOrderDetailDTO> detailDTOList = buildCreateFulfillmentOrderDetailDTOList(origin.getDetailList(), current.getDetailList(), currentAllocateOrder, syncRequest);
            if (CollectionUtils.isEmpty(detailDTOList)){
                log.info("buildCreateFulfillmentOrderDetailDTOList_is_empty");
                return;
            }
            CreateFulfillmentOrderReq req = buildCreateFulfillmentOrderReq(currentAllocateOrder);
            req.setDetailList(detailDTOList);
            log.info("syncAllocateOrder_create_order_request:{}",JSON.toJSONString(req));
            com.alibaba.cloud.retail.delivery.api.model.delivery.response.Result<FulfillmentOrderResponse> order = fulfillmentOrderAbilityService.createOrder(req);
            log.info("syncAllocateOrder_create_order_response:{}",JSON.toJSONString(order));
            if (order != null && order.isSuccess()){
                Map<String, String> bizFeaturesMap = FeatureUtil.fromString(currentAllocateOrder.getBizFeatures());
                String fulfillmentNo = Optional.ofNullable(order).map(com.alibaba.cloud.retail.delivery.api.model.delivery.response.Result::getResult)
                        .map(FulfillmentOrderResponse::getFulfillmentOrderNo).orElse(null);
                if (StringUtils.isNotBlank(fulfillmentNo)){
                    if (StringUtils.isNotBlank(bizFeaturesMap.get("forwardFulfillmentOrderNo"))){
                        bizFeaturesMap.put("forwardFulfillmentOrderNo",bizFeaturesMap.get("forwardFulfillmentOrderNo") + "," + fulfillmentNo);
                    }else {
                        bizFeaturesMap.put("forwardFulfillmentOrderNo",fulfillmentNo);
                    }
                    com.epoch.app.crunifiedinventory.domain.allocateorder.dto.UpdateFeaturesRequest updateFeaturesRequest = new com.epoch.app.crunifiedinventory.domain.allocateorder.dto.UpdateFeaturesRequest();
                    updateFeaturesRequest.setMainOrderCode(currentAllocateOrder.getCode());
                    updateFeaturesRequest.setBizFeatures(FeatureUtil.toString(bizFeaturesMap));
                    allocateOrderService.updateFeatures(updateFeaturesRequest);
                }
            }
//            //从调拨单中找出此次关闭的行
//            List<AllocateOrderDetail> closeDetailList = Nullable.stream(current.getDetailList())
//                    .filter(currentDetail -> Boolean.TRUE.equals(currentDetail.getClose()))
//                    .filter(currentDetail -> origin.getDetailList().stream()
//                            .filter(originDetail -> Objects.equals(currentDetail.getLineNo(), originDetail.getLineNo()))
//                            .anyMatch(originDetail -> !Boolean.TRUE.equals(originDetail.getClose()))).collect(Collectors.toList());
//            //筛选出的此次关闭的行明细集合
//            if (CollectionUtils.isNotEmpty(closeDetailList)){
//                //查出调拨单所有对应的履约单
//                SearchFulfullmentOrderReq searchFulfullmentOrderReq = new SearchFulfullmentOrderReq();
//                searchFulfullmentOrderReq.setSourceOrderNo(currentAllocateOrder.getCode());
//                searchFulfullmentOrderReq.setFulfillmentDirectionType(FulfillmentDirectionTypeEnum.FORWARD.getValue());
//                com.alibaba.cloud.retail.delivery.api.model.delivery.response.Result<List<FulfillmentGetRes>> searchFulfillmentOrder = fulfillmentOrderAbilityService.searchFulfullmentOrder(searchFulfullmentOrderReq);
//                if (searchFulfillmentOrder != null && CollectionUtils.isNotEmpty(searchFulfillmentOrder.getResult())){
//                    //调拨单关联的所有正向履约单
//                    List<FulfillmentGetRes> fulfillmentGetResList = searchFulfillmentOrder.getResult();
//                    //找出履约单对应的出库单
//                    WmsOrderQueryRequest wmsOrderQueryRequest = new WmsOrderQueryRequest();
//                    wmsOrderQueryRequest.setBizOrderId(currentAllocateOrder.getCode());
//                    wmsOrderQueryRequest.setContainSubOrders(true);
//                    com.alibaba.cz.base.tool.result.Result<WarehousingOrdersQueryResponse> wmsQueryResult = warehousingOrderReadService.queryWmsOrderByOtherOrder(wmsOrderQueryRequest);
//                    log.info("AllocateOrderWriteServiceImpl_close_queryWmsResult:{}",JSON.toJSONString(wmsOrderQueryRequest));
//                    List<WarehousingOrderDTO> warehousingOrderDTOList = Optional.ofNullable(wmsQueryResult)
//                            .map(com.alibaba.cz.base.tool.result.Result::getResult)
//                            .map(WarehousingOrdersQueryResponse::getWarehousingOrders)
//                            .orElse(Lists.newArrayList())
//                            .stream().filter(warehousingOrderDTO -> fulfillmentGetResList.stream().map(FulfillmentGetRes::getFulfillmentOrderNo)
//                                    .anyMatch(fulfillmentNo -> fulfillmentNo.equals(warehousingOrderDTO.getFulfillmentOrderId())))
//                            .collect(Collectors.toList());
//
//
//                    List<FinishReq.FulfillmentOrderInfo> fulfillmentOrderInfos = Lists.newArrayList();
//                    closeDetailList.forEach(closeDetail -> {
//                        //循环每个履约单,
//                        searchFulfillmentOrder.getResult().forEach(fulfillmentGetRes -> {
//
//                            FinishReq.FulfillmentOrderInfo fulfillmentOrderInfo = new FinishReq.FulfillmentOrderInfo();
//                            fulfillmentOrderInfo.setFulfillmentOrderNo(fulfillmentGetRes.getFulfillmentOrderNo());
//                            Nullable.stream(fulfillmentGetRes.getDetailList())
//                                    //过滤出符合关闭的行
//                                    .filter(fulfillmentDetailGetRes -> Objects.equals(closeDetail.getLineNo(),fulfillmentDetailGetRes.getRefOrderLineNo()))
//                                    //过滤出待发货和部分发货.  忽略: 已完成/已取消; 忽略: 待提交/待推送
//                                    .filter(fulfillmentDetailGetRes -> CompareUtil.anyOf(fulfillmentDetailGetRes.getStatus(), FulfillmentOrderStatusEnum.WAIT_SEND.getValue().toString(),FulfillmentOrderStatusEnum.PART_SEND.getValue().toString()))
//                                    .forEach(fulfillmentDetailGetRes -> {
//                                        List<WarehousingOrderDTO> matchWmsOrderList = warehousingOrderDTOList.stream()
//                                                //针对这个履约单,匹配出对应的wms主单
//                                                .filter(warehousingOrderDTO -> fulfillmentGetRes.getFulfillmentOrderNo().equals(warehousingOrderDTO.getFulfillmentOrderId()))
//                                                .collect(Collectors.toList());
//                                        if (CollectionUtils.isEmpty(matchWmsOrderList)){
//                                            fulfillmentOrderInfo.setFulfillmentSubOrderNo(fulfillmentDetailGetRes.getLineNumber());
//                                            fulfillmentOrderInfos.add(fulfillmentOrderInfo);
//                                        }else {
//                                            BigDecimal sumWmsQuantity = matchWmsOrderList.stream().map(WarehousingOrderDTO::getWmsOrderDetailList).filter(CollectionUtils::isNotEmpty)
//                                                    .flatMap(Collection::stream).map(WmsOrderDetailDTO::getGoodsQuantity)
//                                                    .filter(Objects::nonNull).map(BigDecimal::new).map(q -> q.movePointLeft(3))
//                                                    .reduce(BigDecimal.ZERO, BigDecimal::add);
//                                            if (fulfillmentDetailGetRes.getQuantity().compareTo(sumWmsQuantity) > 0){
//                                                fulfillmentOrderInfo.setFulfillmentSubOrderNo(fulfillmentDetailGetRes.getLineNumber());
//                                                fulfillmentOrderInfos.add(fulfillmentOrderInfo);
//                                            }
//                                        }
//                                    });
//
//                        });
//                    });
//                    //关闭逻辑
//                    FinishReq finishReq = new FinishReq();
//                    finishReq.setBusinessType(currentAllocateOrder.getBusinessType());
//                    finishReq.setFromSys(currentAllocateOrder.getFromSys());
////                    finishReq.setOperatorCode("test");
//                    //请求履约批量关闭
//                    finishReq.setFulfillmentOrderInfoList(fulfillmentOrderInfos);
//                    log.info("AllocateOrderWriteServiceImpl_close_finish_request:{}",JSON.toJSONString(finishReq));
//                    com.alibaba.cloud.retail.delivery.api.model.delivery.response.Result<List<BatchCloseFulfillmentOrderResponse>> finish =
//                            fulfillmentOrderAbilityService.finish(finishReq);
//                    log.info("AllocateOrderWriteServiceImpl_close_finish_result:{}",JSON.toJSONString(finish));
//                }
//                //没有履约单, 关闭调拨单
//                AllocateOrderCloseRequest allocateOrderCloseRequest = new AllocateOrderCloseRequest();
//                allocateOrderCloseRequest.setOperatorId("sap");
//                List<AllocateOrderCloseDTO> allocateOrderCloseDTOList = Lists.newArrayList();
//                allocateOrderCloseRequest.setCloseList(allocateOrderCloseDTOList);
//                closeDetailList.forEach(closeDetail -> {
//                    AllocateOrderCloseDTO allocateOrderCloseDTO = new AllocateOrderCloseDTO();
//                    allocateOrderCloseDTO.setCloseCode(closeDetail.getCloseCode());
//                    allocateOrderCloseDTO.setOutAllocateOrderLineNo(closeDetail.getOutAllocateOrderLineId());
//                    allocateOrderCloseDTO.setTransferOrderCode(currentAllocateOrder.getCode());
//                    allocateOrderCloseDTO.setDetailNo(closeDetail.getLineNo() == null ? null :closeDetail.getLineNo().toString());
//                    allocateOrderCloseDTOList.add(allocateOrderCloseDTO);
//                });
//                log.info("AllocateOrderWriteServiceImpl_close_closeAllocateOrder_request:{}",JSON.toJSONString(allocateOrderCloseRequest));
//                AllocateOrderCloseResponse allocateOrderCloseResponse = closeAllocateOrder(allocateOrderCloseRequest);
//                log.info("AllocateOrderWriteServiceImpl_close_closeAllocateOrder_result:{}",JSON.toJSONString(allocateOrderCloseResponse));
//            }
        }
    }

    /**
     * 008同步时关闭预报单和调拨单
     * @param origin
     * @param current
     */
    private void closeFulfillmentOrderAndAllocateOrder(com.alibaba.citrus.cr.unified.inventory.model.AllocateOrderEntity origin,
                                                       com.alibaba.citrus.cr.unified.inventory.model.AllocateOrderEntity current){
        //从调拨单中找出此次关闭的行
        List<AllocateOrderDetail> closeDetailList = Nullable.stream(current.getDetailList())
                .filter(currentDetail -> Boolean.TRUE.equals(currentDetail.getClose()))
                .filter(currentDetail -> origin.getDetailList().stream()
                        .filter(originDetail -> Objects.equals(currentDetail.getLineNo(), originDetail.getLineNo()))
                        .anyMatch(originDetail -> !Boolean.TRUE.equals(originDetail.getClose())))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(closeDetailList)){
            log.info("AllocateOrderWriteServiceImpl_close_not_need_close");
            return;
        }
        log.info("AllocateOrderWriteServiceImpl_close_filter_list:{}",JSON.toJSONString(closeDetailList));
        //查出调拨单所有对应的履约单
        AllocateOrder currentAllocateOrder = current.getAllocateOrder();
        SearchFulfullmentOrderReq searchFulfullmentOrderReq = new SearchFulfullmentOrderReq();
        searchFulfullmentOrderReq.setSourceOrderNo(currentAllocateOrder.getCode());
        searchFulfullmentOrderReq.setFulfillmentDirectionType(FulfillmentDirectionTypeEnum.FORWARD.getValue());
        com.alibaba.cloud.retail.delivery.api.model.delivery.response.Result<List<FulfillmentGetRes>> searchFulfillmentOrder = fulfillmentOrderAbilityService.searchFulfullmentOrder(searchFulfullmentOrderReq);
        log.info("AllocateOrderWriteServiceImpl_close_searchFulfillmentOrder_result:{}",JSON.toJSONString(searchFulfillmentOrder));
        if (searchFulfillmentOrder != null && CollectionUtils.isNotEmpty(searchFulfillmentOrder.getResult())){
            //调拨单关联的所有正向履约单
            List<FulfillmentGetRes> fulfillmentGetResList = searchFulfillmentOrder.getResult();
            //找出履约单对应的出库单
            WmsOrderQueryRequest wmsOrderQueryRequest = new WmsOrderQueryRequest();
            wmsOrderQueryRequest.setBizOrderId(currentAllocateOrder.getCode());
            wmsOrderQueryRequest.setContainSubOrders(true);
            com.alibaba.cz.base.tool.result.Result<WarehousingOrdersQueryResponse> wmsQueryResult = warehousingOrderReadService.queryWmsOrderByOtherOrder(wmsOrderQueryRequest);
            log.info("AllocateOrderWriteServiceImpl_close_queryWmsResult:{}",JSON.toJSONString(wmsQueryResult));
            List<WarehousingOrderDTO> warehousingOrderDTOList = Optional.ofNullable(wmsQueryResult)
                    .map(com.alibaba.cz.base.tool.result.Result::getResult)
                    .map(WarehousingOrdersQueryResponse::getWarehousingOrders)
                    .orElse(Lists.newArrayList())
                    .stream().filter(warehousingOrderDTO -> fulfillmentGetResList.stream().map(FulfillmentGetRes::getFulfillmentOrderNo)
                            .anyMatch(fulfillmentNo -> fulfillmentNo.equals(warehousingOrderDTO.getFulfillmentOrderId())))
                    .collect(Collectors.toList());
            log.info("AllocateOrderWriteServiceImpl_close_filter_wms:{}",JSON.toJSONString(warehousingOrderDTOList));
            FinishReq finishReq = buildCloseFulfillmentOrderReq(currentAllocateOrder,
                    closeDetailList,
                    fulfillmentGetResList,
                    warehousingOrderDTOList);
            log.info("AllocateOrderWriteServiceImpl_close_finish_request:{}",JSON.toJSONString(finishReq));
            if (finishReq != null && CollectionUtils.isNotEmpty(finishReq.getFulfillmentOrderInfoList())){
                com.alibaba.cloud.retail.delivery.api.model.delivery.response.Result<List<BatchCloseFulfillmentOrderResponse>> finish =
                        fulfillmentOrderAbilityService.finish(finishReq);
                log.info("AllocateOrderWriteServiceImpl_close_finish_result:{}",JSON.toJSONString(finish));
            }
        }
        //关闭逻辑
        closeAllocateOrder(closeDetailList,currentAllocateOrder);
    }

    private void closeAllocateOrder(List<AllocateOrderDetail> closeDetailList,AllocateOrder currentAllocateOrder){
        //没有履约单, 关闭调拨单
//        AllocateOrderCloseRequest allocateOrderCloseRequest = new AllocateOrderCloseRequest();
//        allocateOrderCloseRequest.setOperatorId("sap");
//        List<AllocateOrderCloseDTO> allocateOrderCloseDTOList = Lists.newArrayList();
//        allocateOrderCloseRequest.setCloseList(allocateOrderCloseDTOList);
//        closeDetailList.forEach(closeDetail -> {
//            AllocateOrderCloseDTO allocateOrderCloseDTO = new AllocateOrderCloseDTO();
//            allocateOrderCloseDTO.setCloseCode(StringUtils.isBlank(closeDetail.getCloseCode()) ? "14" : closeDetail.getCloseCode());
//            allocateOrderCloseDTO.setOutAllocateOrderLineNo(closeDetail.getOutAllocateOrderLineId());
//            allocateOrderCloseDTO.setTransferOrderCode(currentAllocateOrder.getCode());
//            allocateOrderCloseDTO.setDetailNo(closeDetail.getLineNo() == null ? null :closeDetail.getLineNo().toString());
//            allocateOrderCloseDTOList.add(allocateOrderCloseDTO);
//        });
//        log.info("AllocateOrderWriteServiceImpl_close_closeAllocateOrder_request:{}",JSON.toJSONString(allocateOrderCloseRequest));
        TransferOrderCloseRequest transferOrderCloseRequest = new TransferOrderCloseRequest();
        transferOrderCloseRequest.setOperatorId("sap");
        transferOrderCloseRequest.setBusinessType(currentAllocateOrder.getBusinessType());
        transferOrderCloseRequest.setFromSys(currentAllocateOrder.getFromSys());
        List<TransferOrderCloseDetail> closeList = Lists.newArrayList();
        closeDetailList.forEach(closeDetail -> {
            TransferOrderCloseDetail transferOrderCloseDetail = new TransferOrderCloseDetail();
            transferOrderCloseDetail.setCloseCode(StringUtils.isBlank(closeDetail.getCloseCode()) ? "14" : closeDetail.getCloseCode());
//            transferOrderCloseDetail.setDetailNo(closeDetail.getLineNo() == null ? null : closeDetail.getLineNo());
            transferOrderCloseDetail.setTransferOrderCode(currentAllocateOrder.getCode());
            transferOrderCloseDetail.setDetailNo(closeDetail.getLineNo() == null ? null :closeDetail.getLineNo().toString());
            closeList.add(transferOrderCloseDetail);
        });
        transferOrderCloseRequest.setCloseList(closeList);
        log.info("AllocateOrderWriteServiceImpl_close_closeAllocateOrder_request:{}",JSON.toJSONString(transferOrderCloseRequest));
        Result<TransferOrderCloseResponse> transferOrderCloseResponseResult = allocateOrderCloseService.closeAllocateOrder(transferOrderCloseRequest);
//        AllocateOrderCloseResponse allocateOrderCloseResponse = closeAllocateOrder(allocateOrderCloseRequest);
        log.info("AllocateOrderWriteServiceImpl_close_closeAllocateOrder_result:{}",JSON.toJSONString(transferOrderCloseResponseResult));

    }

    private FinishReq buildCloseFulfillmentOrderReq(AllocateOrder currentAllocateOrder,
                                                    List<AllocateOrderDetail> closeDetailList,
                                                    List<FulfillmentGetRes> fulfillmentGetResList,
                                                    List<WarehousingOrderDTO> warehousingOrderDTOList){
        FinishReq finishReq = new FinishReq();
        finishReq.setBusinessType(currentAllocateOrder.getBusinessType());
        finishReq.setFromSys(currentAllocateOrder.getFromSys());
        //TODO 是否是一直6000
        finishReq.setBusinessUnitCode("6000");
        finishReq.setUpdater("system");
        List<FinishReq.FulfillmentOrderInfo> fulfillmentOrderInfos = Lists.newArrayList();
        //请求履约批量关闭
        finishReq.setFulfillmentOrderInfoList(fulfillmentOrderInfos);
        closeDetailList.forEach(closeDetail -> {
            log.info("forEach_closeDetail:{}",JSON.toJSONString(closeDetail));
            //循环每个履约单,
            fulfillmentGetResList.forEach(fulfillmentGetRes -> {
                buildCloseFulfillmentOrderDetailReqList(fulfillmentGetRes,
                        closeDetail,
                        warehousingOrderDTOList,
                        fulfillmentOrderInfos);
//                FinishReq.FulfillmentOrderInfo fulfillmentOrderInfo = new FinishReq.FulfillmentOrderInfo();
//                fulfillmentOrderInfo.setFulfillmentOrderNo(fulfillmentGetRes.getFulfillmentOrderNo());
//                Nullable.stream(fulfillmentGetRes.getDetailList())
//                        //过滤出符合关闭的行
//                        .filter(fulfillmentDetailGetRes -> Objects.equals(closeDetail.getLineNo(),fulfillmentDetailGetRes.getRefOrderLineNo()))
//                        //过滤出待发货和部分发货.  忽略: 已完成/已取消; 忽略: 待提交/待推送
//                        .filter(fulfillmentDetailGetRes -> CompareUtil.anyOf(fulfillmentDetailGetRes.getStatus(), FulfillmentOrderStatusEnum.WAIT_SEND.getValue().toString(),FulfillmentOrderStatusEnum.PART_SEND.getValue().toString()))
//                        .forEach(fulfillmentDetailGetRes -> {
//                            List<WarehousingOrderDTO> matchWmsOrderList = warehousingOrderDTOList.stream()
//                                    //针对这个履约单,匹配出对应的wms主单
//                                    .filter(warehousingOrderDTO -> fulfillmentGetRes.getFulfillmentOrderNo().equals(warehousingOrderDTO.getFulfillmentOrderId()))
//                                    .collect(Collectors.toList());
//                            if (CollectionUtils.isEmpty(matchWmsOrderList)){
//                                fulfillmentOrderInfo.setFulfillmentSubOrderNo(fulfillmentDetailGetRes.getLineNumber());
//                                fulfillmentOrderInfos.add(fulfillmentOrderInfo);
//                            }else {
//                                BigDecimal sumWmsQuantity = matchWmsOrderList.stream().map(WarehousingOrderDTO::getWmsOrderDetailList).filter(CollectionUtils::isNotEmpty)
//                                        .flatMap(Collection::stream).map(WmsOrderDetailDTO::getGoodsQuantity)
//                                        .filter(Objects::nonNull).map(BigDecimal::new).map(q -> q.movePointLeft(3))
//                                        .reduce(BigDecimal.ZERO, BigDecimal::add);
//                                if (fulfillmentDetailGetRes.getQuantity().compareTo(sumWmsQuantity) > 0){
//                                    fulfillmentOrderInfo.setFulfillmentSubOrderNo(fulfillmentDetailGetRes.getLineNumber());
//                                    fulfillmentOrderInfos.add(fulfillmentOrderInfo);
//                                }
//                            }
//                        });

            });
        });
        return finishReq;
    }

    private void buildCloseFulfillmentOrderDetailReqList(FulfillmentGetRes fulfillmentGetRes,
                                                         AllocateOrderDetail closeDetail,
                                                         List<WarehousingOrderDTO> warehousingOrderDTOList,
                                                         List<FinishReq.FulfillmentOrderInfo> fulfillmentOrderInfos
    ){
        log.info("buildCloseFulfillmentOrderDetailReqList,fulfillmentGetRes:{},closeDetail:{},warehousingOrderDTOList:{},fulfillmentOrderInfos:{}",
                JSON.toJSONString(fulfillmentGetRes),JSON.toJSONString(closeDetail),JSON.toJSONString(warehousingOrderDTOList),JSON.toJSONString(fulfillmentOrderInfos));
        FinishReq.FulfillmentOrderInfo fulfillmentOrderInfo = new FinishReq.FulfillmentOrderInfo();
        fulfillmentOrderInfo.setFulfillmentOrderNo(fulfillmentGetRes.getFulfillmentOrderNo());
        Nullable.stream(fulfillmentGetRes.getDetailList())
                //过滤出符合关闭的行
                .filter(fulfillmentDetailGetRes -> Objects.equals(closeDetail.getLineNo() == null ? null : closeDetail.getLineNo().toString(),fulfillmentDetailGetRes.getRefOrderLineNo()))
                //过滤出待发货和部分发货.  忽略: 已完成/已取消; 忽略: 待提交/待推送
                .filter(fulfillmentDetailGetRes -> CompareUtil.anyOf(fulfillmentDetailGetRes.getStatus(), FulfillmentOrderStatusEnum.WAIT_SEND.getValue().toString(),FulfillmentOrderStatusEnum.PART_SEND.getValue().toString()))
                .forEach(fulfillmentDetailGetRes -> {
                    List<WarehousingOrderDTO> matchWmsOrderList = warehousingOrderDTOList.stream()
                            //针对这个履约单,匹配出对应的wms主单
                            .filter(warehousingOrderDTO -> fulfillmentGetRes.getFulfillmentOrderNo().equals(warehousingOrderDTO.getFulfillmentOrderId()))
                            .collect(Collectors.toList());
                    if (CollectionUtils.isEmpty(matchWmsOrderList)){
                        log.info("AllocateOrderWriteServiceImpl_close_not_match_wms:{}",JSON.toJSONString(fulfillmentDetailGetRes));
                        fulfillmentOrderInfo.setFulfillmentSubOrderNo(fulfillmentDetailGetRes.getLineNumber());
                        fulfillmentOrderInfos.add(fulfillmentOrderInfo);
                    }else {
                        //所有wms明细1,
                        BigDecimal sumWmsQuantity = matchWmsOrderList.stream().map(WarehousingOrderDTO::getWmsOrderDetailList).filter(CollectionUtils::isNotEmpty)
                                .flatMap(Collection::stream)
                                .filter(w -> Objects.equals(w.getFulfillmentSubOrderId(),fulfillmentDetailGetRes.getLineNumber() == null ? null: fulfillmentDetailGetRes.getLineNumber().toString()))
                                .map(WmsOrderDetailDTO::getGoodsQuantity)
                                .filter(Objects::nonNull).map(BigDecimal::new)
                                //数量查出来时已经缩小了1000倍
//                                .map(q -> q.movePointLeft(3))
                                .reduce(BigDecimal.ZERO, BigDecimal::add);
                        log.info("quantity:{},sumWmsQuantity:{}",JSON.toJSONString(fulfillmentDetailGetRes.getQuantity()),JSON.toJSONString(sumWmsQuantity));
                        if (fulfillmentDetailGetRes.getQuantity().compareTo(sumWmsQuantity) > 0){
                            log.info("AllocateOrderWriteServiceImpl_close_more_than:{}",JSON.toJSONString(fulfillmentDetailGetRes));
                            fulfillmentOrderInfo.setFulfillmentSubOrderNo(fulfillmentDetailGetRes.getLineNumber());
                            fulfillmentOrderInfos.add(fulfillmentOrderInfo);
                        }
                        log.info("AllocateOrderWriteServiceImpl_close_less_than_or_equal:{}",JSON.toJSONString(fulfillmentDetailGetRes));
                    }
                });

    }

    /**
     * 构建履约单明细
     * 008操作前的调拨单明细
     */
    public List<CreateFulfillmentOrderDetailDTO> buildCreateFulfillmentOrderDetailDTOList(List<AllocateOrderDetail> originDetailList,
                                                                                          List<AllocateOrderDetail> currentDetailList,
                                                                                          AllocateOrder currentAllocateOrder,
                                                                                          AllocateOrderSyncRequest syncRequest){
        log.info("originDetailList:{},currentDetailList:{},syncRequest:{}",JSON.toJSONString(originDetailList),JSON.toJSONString(currentDetailList),JSON.toJSONString(syncRequest));
        List<AllocateOrderScItemDetailDTO> existConfirmQuantityDetailList = syncRequest.getAllocateOrderScItemDetails().stream()
                .filter(detailDTO -> StringUtils.isNotBlank(detailDTO.getConfirmQuantity())
                        && new BigDecimal(detailDTO.getConfirmQuantity().trim()).compareTo(BigDecimal.ZERO) > 0)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(existConfirmQuantityDetailList)){
            //如果008没有一行是有确认数量的则跳过
            log.info("buildCreateFulfillmentOrderDetailDTOList_not_exist_confirmQuantity");
            return null;
        }
        List<CreateFulfillmentOrderDetailDTO> detailReqList = Lists.newArrayList();
        for (AllocateOrderDetail currentDetail : currentDetailList) {
            AllocateOrderScItemDetailDTO syncDetail = existConfirmQuantityDetailList.stream().filter(detailDTO ->
                            Objects.equals(InfoUtil.deleteZeroPrefix(detailDTO.getOutAllocateOrderLineId()), currentDetail.getOutAllocateOrderLineId()))
                    .findFirst().orElse(null);
            if (syncDetail == null){
                //这里是匹配外部行号的, 有确认数量的才需要创建履约单
                log.info("buildCreateFulfillmentOrderDetailDTOList_outAllocateOrderLineId_not_match:{}",JSON.toJSONString(currentDetail));
                continue;
            }
            AllocateOrderDetail originDetail = originDetailList.stream().filter(allocateOrderDetail -> Objects.equals(allocateOrderDetail.getLineNo(), currentDetail.getLineNo()))
                    .findFirst().orElse(null);
            if (originDetail == null){
                //TODO 这里作为新增行, 可能需要创建履约单,先跳过.
                log.info("buildCreateFulfillmentOrderDetailDTOList_new_line:{}",JSON.toJSONString(currentDetail));
                continue;
            }
            //获取数量
            BigDecimal originConfirmQuantity = Optional.ofNullable(originDetail.getConfirmQuantity()).map(String::trim).map(BigDecimal::new).orElse(BigDecimal.ZERO);
            BigDecimal currentConfirmQuantity = Optional.ofNullable(currentDetail.getConfirmQuantity()).map(String::trim).map(BigDecimal::new).orElse(BigDecimal.ZERO);
            BigDecimal quantity = currentConfirmQuantity.subtract(originConfirmQuantity);
            log.info("originConfirmQuantity:{},currentConfirmQuantity:{},quantity:{}",JSON.toJSONString(originConfirmQuantity),
                    JSON.toJSONString(currentConfirmQuantity),JSON.toJSONString(quantity));
            if (quantity.compareTo(BigDecimal.ZERO) <= 0){
                log.info("compare_confirm_quantity_less_than");
                continue;
            }

            CreateFulfillmentOrderDetailDTO detailReq = new CreateFulfillmentOrderDetailDTO();
            detailReq.setSourceOrderLineNo(currentDetail.getLineNo() == null ? null : currentDetail.getLineNo().toString());
            detailReq.setRefOrderNo(currentAllocateOrder.getCode());
            detailReq.setRefOrderLineNo(currentDetail.getLineNo() == null ? null : currentDetail.getLineNo().toString());
            detailReq.setMaterialCode(currentDetail.getScItemCode());
            detailReq.setRefOrderLineNo(currentDetail.getLineNo() == null ? null : currentDetail.getLineNo().toString());
            detailReq.setRefOrderCreateTime(currentAllocateOrder.getGmt_create() == null ? null :
                    DateFormatUtil.toString(currentAllocateOrder.getGmt_create(),"yyyy-MM-dd HH:mm:ss"));
            detailReq.setProductionDate(currentDetail.getExpectedProduceDate() == null ? null :
                    DateFormatUtil.toString(currentAllocateOrder.getExpectedDeliveryDate(),"yyyy-MM-dd"));
            detailReq.setSalesUnitCode(StringUtils.isBlank(currentDetail.getUnit()) ? "PC" : currentDetail.getUnit());
            detailReq.setSaleQuantity(quantity);
            detailReq.setQuantity(quantity);
            detailReq.setShippingUnitCode(StringUtils.isBlank(currentDetail.getUnit()) ? "PC" : currentDetail.getUnit());
            detailReq.setRemark(currentDetail.getRemarks());
            detailReq.setClose(Boolean.TRUE.equals(currentDetail.getClose()) ? 1 : 0);
            detailReq.setCloseReasonCode(currentDetail.getCloseCode());
            detailReq.setCloseReason(currentDetail.getCloseReason());
            Map<String,String> featuresMap = Maps.newHashMap();
            if (StringUtils.isNotBlank(currentDetail.getOutAllocateOrderLineId())){
                featuresMap.put(FeaturesKey.OUT_ALLOCATE_ORDER_LINE_ID,currentDetail.getOutAllocateOrderLineId());
            }
            detailReq.setFeatures(FeatureUtil.toString(featuresMap));
            detailReqList.add(detailReq);
        }
        return detailReqList;
    }

    /**
     * 构建履约单主单
     * @param allocateOrder
     * @return
     */
    public CreateFulfillmentOrderReq buildCreateFulfillmentOrderReq(AllocateOrder allocateOrder){
        CreateFulfillmentOrderReq req = new CreateFulfillmentOrderReq();
        if (StringUtils.isNotBlank(allocateOrder.getFromSys())){
            req.setFromSys(allocateOrder.getFromSys());
        }else {
            req.setFromSys("APP_SAP");
        }
        req.setBusinessType(StringUtils.isNotBlank(allocateOrder.getBusinessType()) ? allocateOrder.getBusinessType() : "11");
        //TODO 业务单据编码
        req.setBusinessUnitCode("6000");
        req.setRequestNo(UUID.randomUUID().toString().replaceAll("-",""));
        //调拨单号
        req.setSourceOrderNo(allocateOrder.getCode());
        //退单:调拨退单出库,  否:调拨出库
        if (Boolean.TRUE.equals(allocateOrder.getReturnOrder())){
            req.setRefOrderType(OrderTypeEnum.ALLOCATE_RETURN_OUTBOUND.getValue());
        }else {
            req.setRefOrderType(OrderTypeEnum.ALLOCATE_OUTBOUND.getValue());
        }
        //调出仓和调入仓属于同一个物理仓（虚拟移库） 更正逻辑改为如果调拨单创建时履约类型为虚拟移库,则这里传递为虚拟移库
//        if (Objects.equals(allocateOrder.getShipWarehouseId(),allocateOrder.getReceiveWarehouseId())){
        if (com.epoch.app.crunifiedinventory.model.enums.FulfillmentTypeEnum.VIRTUAL_TRANSFER.getValue().toString().equals(allocateOrder.getFulfillmentTypeCode())){
            req.setFulfillmentType(FulfillmentTypeEnum.EXTERNALWAREHOUSE_FULFILLMENT.getValue());
        }else {
            req.setFulfillmentType(FulfillmentTypeEnum.BIGWAREHOUSE_FULFILLMENT.getValue());
        }
        req.setAutoSubmit(Boolean.TRUE);
        //枚举：：1=普通，100=优先
        req.setPriority(allocateOrder.getPriority() == null ? "1" : allocateOrder.getPriority().toString());
        req.setProductGroupCode(allocateOrder.getProductGroup());
        Map<String, CargoZoneSDO> cargoZoneSDOMap = this.getCargoZoneSDOMap(Arrays.asList(allocateOrder.getShipAreaId(), allocateOrder.getReceiveAreaId(), allocateOrder.getShipWarehouseId(), allocateOrder.getReceiveWarehouseId()).stream().filter(StringUtils::isNotBlank)
                .collect(Collectors.toList()));
        Map<String, CargoZoneSDO> codeZoneSDOMap = this.getCargoZoneSDOMapByCodes(Arrays.asList(allocateOrder.getTransferWarehouseId()).stream().filter(StringUtils::isNotBlank).collect(Collectors.toList()));
        String logicShippingWarehouseCode = Optional.ofNullable(cargoZoneSDOMap.get(allocateOrder.getShipAreaId())).map(CargoZoneSDO::getCode).orElse(null);
        String logicShippingWarehouseName = Optional.ofNullable(cargoZoneSDOMap.get(allocateOrder.getShipAreaId())).map(CargoZoneSDO::getName).orElse(null);
        String shippingWarehouseCode = Optional.ofNullable(cargoZoneSDOMap.get(allocateOrder.getShipWarehouseId())).map(CargoZoneSDO::getCode).orElse(null);
        String shippingWarehouseName = Optional.ofNullable(cargoZoneSDOMap.get(allocateOrder.getShipWarehouseId())).map(CargoZoneSDO::getName).orElse(null);
        String logicReceivingWarehouseCode = Optional.ofNullable(cargoZoneSDOMap.get(allocateOrder.getReceiveAreaId())).map(CargoZoneSDO::getCode).orElse(null);
        String logicReceivingWarehouseName = Optional.ofNullable(cargoZoneSDOMap.get(allocateOrder.getReceiveAreaId())).map(CargoZoneSDO::getName).orElse(null);
        String receivingWarehouseCode = Optional.ofNullable(cargoZoneSDOMap.get(allocateOrder.getReceiveWarehouseId())).map(CargoZoneSDO::getCode).orElse(null);
        String receivingWarehouseName = Optional.ofNullable(cargoZoneSDOMap.get(allocateOrder.getReceiveWarehouseId())).map(CargoZoneSDO::getName).orElse(null);
        String transferWarehouseName = Optional.ofNullable(codeZoneSDOMap.get(allocateOrder.getTransferWarehouseId())).map(CargoZoneSDO::getName).orElse(null);
        Map<String,String> features = Maps.newHashMap();
        features.put(FeaturesKey.OUT_ALLOCATE_ORDER_ID,allocateOrder.getOutAllocateOrderId());
        if (Boolean.TRUE.equals(allocateOrder.getReturnOrder())){
            req.setLogicReceivingWarehouseCode(logicShippingWarehouseCode);
            req.setLogicReceivingWarehouse(logicShippingWarehouseName);
            req.setReceivingWarehouseCode(shippingWarehouseCode);
            req.setReceivingWarehouse(shippingWarehouseName);
            features.put(FeaturesKey.TRANSFER_ORDER_TARGET_LOGIC_WAREHOUSE_CODE,logicShippingWarehouseCode);
            if (ToolUtil.isTrainOrSea(allocateOrder.getShipMethod())){
                req.setLogicShippingWarehouseCode(allocateOrder.getTransferWarehouseId());
                req.setLogicShippingWarehouse(transferWarehouseName);
            }else {
                req.setLogicShippingWarehouseCode(logicReceivingWarehouseCode);
                req.setLogicShippingWarehouse(logicReceivingWarehouseName);
                req.setShippingWarehouseCode(receivingWarehouseCode);
                req.setShippingWarehouse(receivingWarehouseName);
            }
            CargoZoneSDO cargoZoneSDO = cargoZoneSDOMap.get(allocateOrder.getReceiveAreaId());
            if (cargoZoneSDO != null && cargoZoneSDO.getFeature() != null){
                req.setSoldToPartyCode(cargoZoneSDO.getFeature().get("sapInternalSalesBPCode"));
                req.setShipToPartyCode(cargoZoneSDO.getFeature().get("sapInternalDeliveryBPCode"));
            }
        }else {
            req.setLogicShippingWarehouseCode(logicShippingWarehouseCode);
            req.setLogicShippingWarehouse(logicShippingWarehouseName);
            req.setShippingWarehouseCode(shippingWarehouseCode);
            req.setShippingWarehouse(shippingWarehouseName);
            features.put(FeaturesKey.TRANSFER_ORDER_TARGET_LOGIC_WAREHOUSE_CODE,logicReceivingWarehouseCode);
            if (ToolUtil.isTrainOrSea(allocateOrder.getShipMethod())){
                req.setLogicReceivingWarehouseCode(allocateOrder.getTransferWarehouseId());
                req.setLogicReceivingWarehouse(transferWarehouseName);
//                CargoZoneSDO cargoZoneSDO = codeZoneSDOMap.get(allocateOrder.getTransferWarehouseId());
                CargoZoneSDO cargoZoneSDO = cargoZoneSDOMap.get(allocateOrder.getReceiveAreaId());
                if (cargoZoneSDO != null && MapUtils.isNotEmpty(cargoZoneSDO.getFeature())){
                    req.setSoldToPartyCode(cargoZoneSDO.getFeature().get("sapInternalSalesBPCode"));
                    req.setShipToPartyCode(cargoZoneSDO.getFeature().get("sapInternalDeliveryBPCode"));
                }
            }else {
                req.setLogicReceivingWarehouseCode(logicReceivingWarehouseCode);
                req.setLogicReceivingWarehouse(logicReceivingWarehouseName);
                req.setReceivingWarehouseCode(receivingWarehouseCode);
                req.setReceivingWarehouse(receivingWarehouseName);
                //客户售达方编码 来自于仓库上的售达方编码
                CargoZoneSDO cargoZoneSDO = cargoZoneSDOMap.get(allocateOrder.getReceiveAreaId());
                if (cargoZoneSDO != null && MapUtils.isNotEmpty(cargoZoneSDO.getFeature())){
                    req.setSoldToPartyCode(cargoZoneSDO.getFeature().get("sapInternalSalesBPCode"));
                    req.setShipToPartyCode(cargoZoneSDO.getFeature().get("sapInternalDeliveryBPCode"));
                }
            }
        }
        //可以后移
        if (MapUtils.isNotEmpty(features)){
            req.setFeatures(FeatureUtil.toString(features));
        }
        req.setProductGroupCode(StringUtils.isNotBlank(allocateOrder.getProductGroup()) ? allocateOrder.getProductGroup() : "11");
        req.setChannelCode("90");
        //6000?
        req.setSalesOrganizationCode("6000");
        //TODO 送达方 售达方 选择仓库上的, 待定
//            req.setShipToPartyCode();
//        req.setCreator(allocateOrder.getCreator());
        req.setExpectedDeliveryTime(Objects.isNull(allocateOrder.getExpectedDeliveryDate()) ? null :
                DateFormatUtil.toString(allocateOrder.getExpectedDeliveryDate(),DateFormatUtil.TIME_PATTERN));
        req.setShippingMethodCode(allocateOrder.getShipMethod() == null ? null : allocateOrder.getShipMethod().toString());
        req.setRemark(allocateOrder.getRemark());
        return req;
    }
}
